1f7018c21STomi Valkeinen /* 2f7018c21STomi Valkeinen * Permedia2 framebuffer driver. 3f7018c21STomi Valkeinen * 4f7018c21STomi Valkeinen * 2.5/2.6 driver: 5f7018c21STomi Valkeinen * Copyright (c) 2003 Jim Hague (jim.hague@acm.org) 6f7018c21STomi Valkeinen * 7f7018c21STomi Valkeinen * based on 2.4 driver: 8f7018c21STomi Valkeinen * Copyright (c) 1998-2000 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) 9f7018c21STomi Valkeinen * Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com) 10f7018c21STomi Valkeinen * 11f7018c21STomi Valkeinen * and additional input from James Simmon's port of Hannu Mallat's tdfx 12f7018c21STomi Valkeinen * driver. 13f7018c21STomi Valkeinen * 14f7018c21STomi Valkeinen * I have a Creative Graphics Blaster Exxtreme card - pm2fb on x86. I 15f7018c21STomi Valkeinen * have no access to other pm2fb implementations. Sparc (and thus 16f7018c21STomi Valkeinen * hopefully other big-endian) devices now work, thanks to a lot of 17f7018c21STomi Valkeinen * testing work by Ron Murray. I have no access to CVision hardware, 18f7018c21STomi Valkeinen * and therefore for now I am omitting the CVision code. 19f7018c21STomi Valkeinen * 20f7018c21STomi Valkeinen * Multiple boards support has been on the TODO list for ages. 21f7018c21STomi Valkeinen * Don't expect this to change. 22f7018c21STomi Valkeinen * 23f7018c21STomi Valkeinen * This file is subject to the terms and conditions of the GNU General Public 24f7018c21STomi Valkeinen * License. See the file COPYING in the main directory of this archive for 25f7018c21STomi Valkeinen * more details. 26f7018c21STomi Valkeinen * 27f7018c21STomi Valkeinen * 28f7018c21STomi Valkeinen */ 29f7018c21STomi Valkeinen 30f7018c21STomi Valkeinen #include <linux/module.h> 31f7018c21STomi Valkeinen #include <linux/moduleparam.h> 32f7018c21STomi Valkeinen #include <linux/kernel.h> 33f7018c21STomi Valkeinen #include <linux/errno.h> 34f7018c21STomi Valkeinen #include <linux/string.h> 35f7018c21STomi Valkeinen #include <linux/mm.h> 36f7018c21STomi Valkeinen #include <linux/slab.h> 37f7018c21STomi Valkeinen #include <linux/delay.h> 38f7018c21STomi Valkeinen #include <linux/fb.h> 39f7018c21STomi Valkeinen #include <linux/init.h> 40f7018c21STomi Valkeinen #include <linux/pci.h> 41f7018c21STomi Valkeinen #include <video/permedia2.h> 42f7018c21STomi Valkeinen #include <video/cvisionppc.h> 43f7018c21STomi Valkeinen 44f7018c21STomi Valkeinen #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) 45f7018c21STomi Valkeinen #error "The endianness of the target host has not been defined." 46f7018c21STomi Valkeinen #endif 47f7018c21STomi Valkeinen 48f7018c21STomi Valkeinen #if !defined(CONFIG_PCI) 49f7018c21STomi Valkeinen #error "Only generic PCI cards supported." 50f7018c21STomi Valkeinen #endif 51f7018c21STomi Valkeinen 52f7018c21STomi Valkeinen #undef PM2FB_MASTER_DEBUG 53f7018c21STomi Valkeinen #ifdef PM2FB_MASTER_DEBUG 54f7018c21STomi Valkeinen #define DPRINTK(a, b...) \ 55f7018c21STomi Valkeinen printk(KERN_DEBUG "pm2fb: %s: " a, __func__ , ## b) 56f7018c21STomi Valkeinen #else 571a56b9faSRandy Dunlap #define DPRINTK(a, b...) no_printk(a, ##b) 58f7018c21STomi Valkeinen #endif 59f7018c21STomi Valkeinen 60f7018c21STomi Valkeinen #define PM2_PIXMAP_SIZE (1600 * 4) 61f7018c21STomi Valkeinen 62f7018c21STomi Valkeinen /* 63f7018c21STomi Valkeinen * Driver data 64f7018c21STomi Valkeinen */ 65f7018c21STomi Valkeinen static int hwcursor = 1; 66f7018c21STomi Valkeinen static char *mode_option; 67f7018c21STomi Valkeinen 68f7018c21STomi Valkeinen /* 69f7018c21STomi Valkeinen * The XFree GLINT driver will (I think to implement hardware cursor 70f7018c21STomi Valkeinen * support on TVP4010 and similar where there is no RAMDAC - see 71f7018c21STomi Valkeinen * comment in set_video) always request +ve sync regardless of what 72f7018c21STomi Valkeinen * the mode requires. This screws me because I have a Sun 73f7018c21STomi Valkeinen * fixed-frequency monitor which absolutely has to have -ve sync. So 74f7018c21STomi Valkeinen * these flags allow the user to specify that requests for +ve sync 75f7018c21STomi Valkeinen * should be silently turned in -ve sync. 76f7018c21STomi Valkeinen */ 77f7018c21STomi Valkeinen static bool lowhsync; 78f7018c21STomi Valkeinen static bool lowvsync; 79f7018c21STomi Valkeinen static bool noaccel; 80f7018c21STomi Valkeinen static bool nomtrr; 81f7018c21STomi Valkeinen 82f7018c21STomi Valkeinen /* 83f7018c21STomi Valkeinen * The hardware state of the graphics card that isn't part of the 84f7018c21STomi Valkeinen * screeninfo. 85f7018c21STomi Valkeinen */ 86f7018c21STomi Valkeinen struct pm2fb_par 87f7018c21STomi Valkeinen { 88f7018c21STomi Valkeinen pm2type_t type; /* Board type */ 89f7018c21STomi Valkeinen unsigned char __iomem *v_regs;/* virtual address of p_regs */ 90f7018c21STomi Valkeinen u32 memclock; /* memclock */ 91f7018c21STomi Valkeinen u32 video; /* video flags before blanking */ 92f7018c21STomi Valkeinen u32 mem_config; /* MemConfig reg at probe */ 93f7018c21STomi Valkeinen u32 mem_control; /* MemControl reg at probe */ 94f7018c21STomi Valkeinen u32 boot_address; /* BootAddress reg at probe */ 95f7018c21STomi Valkeinen u32 palette[16]; 96f8f05cdcSLuis R. Rodriguez int wc_cookie; 97f7018c21STomi Valkeinen }; 98f7018c21STomi Valkeinen 99f7018c21STomi Valkeinen /* 100f7018c21STomi Valkeinen * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo 101f7018c21STomi Valkeinen * if we don't use modedb. 102f7018c21STomi Valkeinen */ 103f7018c21STomi Valkeinen static struct fb_fix_screeninfo pm2fb_fix = { 104f7018c21STomi Valkeinen .id = "", 105f7018c21STomi Valkeinen .type = FB_TYPE_PACKED_PIXELS, 106f7018c21STomi Valkeinen .visual = FB_VISUAL_PSEUDOCOLOR, 107f7018c21STomi Valkeinen .xpanstep = 1, 108f7018c21STomi Valkeinen .ypanstep = 1, 109f7018c21STomi Valkeinen .ywrapstep = 0, 110f7018c21STomi Valkeinen .accel = FB_ACCEL_3DLABS_PERMEDIA2, 111f7018c21STomi Valkeinen }; 112f7018c21STomi Valkeinen 113f7018c21STomi Valkeinen /* 114f7018c21STomi Valkeinen * Default video mode. In case the modedb doesn't work. 115f7018c21STomi Valkeinen */ 116ca9384c5SJulia Lawall static const struct fb_var_screeninfo pm2fb_var = { 117f7018c21STomi Valkeinen /* "640x480, 8 bpp @ 60 Hz */ 118f7018c21STomi Valkeinen .xres = 640, 119f7018c21STomi Valkeinen .yres = 480, 120f7018c21STomi Valkeinen .xres_virtual = 640, 121f7018c21STomi Valkeinen .yres_virtual = 480, 122f7018c21STomi Valkeinen .bits_per_pixel = 8, 123f7018c21STomi Valkeinen .red = {0, 8, 0}, 124f7018c21STomi Valkeinen .blue = {0, 8, 0}, 125f7018c21STomi Valkeinen .green = {0, 8, 0}, 126f7018c21STomi Valkeinen .activate = FB_ACTIVATE_NOW, 127f7018c21STomi Valkeinen .height = -1, 128f7018c21STomi Valkeinen .width = -1, 129f7018c21STomi Valkeinen .accel_flags = 0, 130f7018c21STomi Valkeinen .pixclock = 39721, 131f7018c21STomi Valkeinen .left_margin = 40, 132f7018c21STomi Valkeinen .right_margin = 24, 133f7018c21STomi Valkeinen .upper_margin = 32, 134f7018c21STomi Valkeinen .lower_margin = 11, 135f7018c21STomi Valkeinen .hsync_len = 96, 136f7018c21STomi Valkeinen .vsync_len = 2, 137f7018c21STomi Valkeinen .vmode = FB_VMODE_NONINTERLACED 138f7018c21STomi Valkeinen }; 139f7018c21STomi Valkeinen 140f7018c21STomi Valkeinen /* 141f7018c21STomi Valkeinen * Utility functions 142f7018c21STomi Valkeinen */ 143f7018c21STomi Valkeinen 144f7018c21STomi Valkeinen static inline u32 pm2_RD(struct pm2fb_par *p, s32 off) 145f7018c21STomi Valkeinen { 146f7018c21STomi Valkeinen return fb_readl(p->v_regs + off); 147f7018c21STomi Valkeinen } 148f7018c21STomi Valkeinen 149f7018c21STomi Valkeinen static inline void pm2_WR(struct pm2fb_par *p, s32 off, u32 v) 150f7018c21STomi Valkeinen { 151f7018c21STomi Valkeinen fb_writel(v, p->v_regs + off); 152f7018c21STomi Valkeinen } 153f7018c21STomi Valkeinen 154f7018c21STomi Valkeinen static inline u32 pm2_RDAC_RD(struct pm2fb_par *p, s32 idx) 155f7018c21STomi Valkeinen { 156f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx); 157f7018c21STomi Valkeinen mb(); 158f7018c21STomi Valkeinen return pm2_RD(p, PM2R_RD_INDEXED_DATA); 159f7018c21STomi Valkeinen } 160f7018c21STomi Valkeinen 161f7018c21STomi Valkeinen static inline u32 pm2v_RDAC_RD(struct pm2fb_par *p, s32 idx) 162f7018c21STomi Valkeinen { 163f7018c21STomi Valkeinen pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); 164f7018c21STomi Valkeinen mb(); 165f7018c21STomi Valkeinen return pm2_RD(p, PM2VR_RD_INDEXED_DATA); 166f7018c21STomi Valkeinen } 167f7018c21STomi Valkeinen 168f7018c21STomi Valkeinen static inline void pm2_RDAC_WR(struct pm2fb_par *p, s32 idx, u32 v) 169f7018c21STomi Valkeinen { 170f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx); 171f7018c21STomi Valkeinen wmb(); 172f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_INDEXED_DATA, v); 173f7018c21STomi Valkeinen wmb(); 174f7018c21STomi Valkeinen } 175f7018c21STomi Valkeinen 176f7018c21STomi Valkeinen static inline void pm2v_RDAC_WR(struct pm2fb_par *p, s32 idx, u32 v) 177f7018c21STomi Valkeinen { 178f7018c21STomi Valkeinen pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); 179f7018c21STomi Valkeinen wmb(); 180f7018c21STomi Valkeinen pm2_WR(p, PM2VR_RD_INDEXED_DATA, v); 181f7018c21STomi Valkeinen wmb(); 182f7018c21STomi Valkeinen } 183f7018c21STomi Valkeinen 184f7018c21STomi Valkeinen #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT 185f7018c21STomi Valkeinen #define WAIT_FIFO(p, a) 186f7018c21STomi Valkeinen #else 187f7018c21STomi Valkeinen static inline void WAIT_FIFO(struct pm2fb_par *p, u32 a) 188f7018c21STomi Valkeinen { 189f7018c21STomi Valkeinen while (pm2_RD(p, PM2R_IN_FIFO_SPACE) < a) 190f7018c21STomi Valkeinen cpu_relax(); 191f7018c21STomi Valkeinen } 192f7018c21STomi Valkeinen #endif 193f7018c21STomi Valkeinen 194f7018c21STomi Valkeinen /* 195f7018c21STomi Valkeinen * partial products for the supported horizontal resolutions. 196f7018c21STomi Valkeinen */ 197f7018c21STomi Valkeinen #define PACKPP(p0, p1, p2) (((p2) << 6) | ((p1) << 3) | (p0)) 198f7018c21STomi Valkeinen static const struct { 199f7018c21STomi Valkeinen u16 width; 200f7018c21STomi Valkeinen u16 pp; 201f7018c21STomi Valkeinen } pp_table[] = { 202f7018c21STomi Valkeinen { 32, PACKPP(1, 0, 0) }, { 64, PACKPP(1, 1, 0) }, 203f7018c21STomi Valkeinen { 96, PACKPP(1, 1, 1) }, { 128, PACKPP(2, 1, 1) }, 204f7018c21STomi Valkeinen { 160, PACKPP(2, 2, 1) }, { 192, PACKPP(2, 2, 2) }, 205f7018c21STomi Valkeinen { 224, PACKPP(3, 2, 1) }, { 256, PACKPP(3, 2, 2) }, 206f7018c21STomi Valkeinen { 288, PACKPP(3, 3, 1) }, { 320, PACKPP(3, 3, 2) }, 207f7018c21STomi Valkeinen { 384, PACKPP(3, 3, 3) }, { 416, PACKPP(4, 3, 1) }, 208f7018c21STomi Valkeinen { 448, PACKPP(4, 3, 2) }, { 512, PACKPP(4, 3, 3) }, 209f7018c21STomi Valkeinen { 544, PACKPP(4, 4, 1) }, { 576, PACKPP(4, 4, 2) }, 210f7018c21STomi Valkeinen { 640, PACKPP(4, 4, 3) }, { 768, PACKPP(4, 4, 4) }, 211f7018c21STomi Valkeinen { 800, PACKPP(5, 4, 1) }, { 832, PACKPP(5, 4, 2) }, 212f7018c21STomi Valkeinen { 896, PACKPP(5, 4, 3) }, { 1024, PACKPP(5, 4, 4) }, 213f7018c21STomi Valkeinen { 1056, PACKPP(5, 5, 1) }, { 1088, PACKPP(5, 5, 2) }, 214f7018c21STomi Valkeinen { 1152, PACKPP(5, 5, 3) }, { 1280, PACKPP(5, 5, 4) }, 215f7018c21STomi Valkeinen { 1536, PACKPP(5, 5, 5) }, { 1568, PACKPP(6, 5, 1) }, 216f7018c21STomi Valkeinen { 1600, PACKPP(6, 5, 2) }, { 1664, PACKPP(6, 5, 3) }, 217f7018c21STomi Valkeinen { 1792, PACKPP(6, 5, 4) }, { 2048, PACKPP(6, 5, 5) }, 218f7018c21STomi Valkeinen { 0, 0 } }; 219f7018c21STomi Valkeinen 220f7018c21STomi Valkeinen static u32 partprod(u32 xres) 221f7018c21STomi Valkeinen { 222f7018c21STomi Valkeinen int i; 223f7018c21STomi Valkeinen 224f7018c21STomi Valkeinen for (i = 0; pp_table[i].width && pp_table[i].width != xres; i++) 225f7018c21STomi Valkeinen ; 226f7018c21STomi Valkeinen if (pp_table[i].width == 0) 227f7018c21STomi Valkeinen DPRINTK("invalid width %u\n", xres); 228f7018c21STomi Valkeinen return pp_table[i].pp; 229f7018c21STomi Valkeinen } 230f7018c21STomi Valkeinen 231f7018c21STomi Valkeinen static u32 to3264(u32 timing, int bpp, int is64) 232f7018c21STomi Valkeinen { 233f7018c21STomi Valkeinen switch (bpp) { 234f7018c21STomi Valkeinen case 24: 235f7018c21STomi Valkeinen timing *= 3; 236ad04fae0SGustavo A. R. Silva fallthrough; 237f7018c21STomi Valkeinen case 8: 238f7018c21STomi Valkeinen timing >>= 1; 239ad04fae0SGustavo A. R. Silva fallthrough; 240f7018c21STomi Valkeinen case 16: 241f7018c21STomi Valkeinen timing >>= 1; 24204295bc3SGustavo A. R. Silva fallthrough; 243f7018c21STomi Valkeinen case 32: 244f7018c21STomi Valkeinen break; 245f7018c21STomi Valkeinen } 246f7018c21STomi Valkeinen if (is64) 247f7018c21STomi Valkeinen timing >>= 1; 248f7018c21STomi Valkeinen return timing; 249f7018c21STomi Valkeinen } 250f7018c21STomi Valkeinen 251f7018c21STomi Valkeinen static void pm2_mnp(u32 clk, unsigned char *mm, unsigned char *nn, 252f7018c21STomi Valkeinen unsigned char *pp) 253f7018c21STomi Valkeinen { 254f7018c21STomi Valkeinen unsigned char m; 255f7018c21STomi Valkeinen unsigned char n; 256f7018c21STomi Valkeinen unsigned char p; 257f7018c21STomi Valkeinen u32 f; 258f7018c21STomi Valkeinen s32 curr; 259f7018c21STomi Valkeinen s32 delta = 100000; 260f7018c21STomi Valkeinen 261f7018c21STomi Valkeinen *mm = *nn = *pp = 0; 262f7018c21STomi Valkeinen for (n = 2; n < 15; n++) { 263f7018c21STomi Valkeinen for (m = 2; m; m++) { 264f7018c21STomi Valkeinen f = PM2_REFERENCE_CLOCK * m / n; 265f7018c21STomi Valkeinen if (f >= 150000 && f <= 300000) { 266f7018c21STomi Valkeinen for (p = 0; p < 5; p++, f >>= 1) { 267f7018c21STomi Valkeinen curr = (clk > f) ? clk - f : f - clk; 268f7018c21STomi Valkeinen if (curr < delta) { 269f7018c21STomi Valkeinen delta = curr; 270f7018c21STomi Valkeinen *mm = m; 271f7018c21STomi Valkeinen *nn = n; 272f7018c21STomi Valkeinen *pp = p; 273f7018c21STomi Valkeinen } 274f7018c21STomi Valkeinen } 275f7018c21STomi Valkeinen } 276f7018c21STomi Valkeinen } 277f7018c21STomi Valkeinen } 278f7018c21STomi Valkeinen } 279f7018c21STomi Valkeinen 280f7018c21STomi Valkeinen static void pm2v_mnp(u32 clk, unsigned char *mm, unsigned char *nn, 281f7018c21STomi Valkeinen unsigned char *pp) 282f7018c21STomi Valkeinen { 283f7018c21STomi Valkeinen unsigned char m; 284f7018c21STomi Valkeinen unsigned char n; 285f7018c21STomi Valkeinen unsigned char p; 286f7018c21STomi Valkeinen u32 f; 287f7018c21STomi Valkeinen s32 delta = 1000; 288f7018c21STomi Valkeinen 289f7018c21STomi Valkeinen *mm = *nn = *pp = 0; 290f7018c21STomi Valkeinen for (m = 1; m < 128; m++) { 291f7018c21STomi Valkeinen for (n = 2 * m + 1; n; n++) { 292f7018c21STomi Valkeinen for (p = 0; p < 2; p++) { 293f7018c21STomi Valkeinen f = (PM2_REFERENCE_CLOCK >> (p + 1)) * n / m; 294f7018c21STomi Valkeinen if (clk > f - delta && clk < f + delta) { 295f7018c21STomi Valkeinen delta = (clk > f) ? clk - f : f - clk; 296f7018c21STomi Valkeinen *mm = m; 297f7018c21STomi Valkeinen *nn = n; 298f7018c21STomi Valkeinen *pp = p; 299f7018c21STomi Valkeinen } 300f7018c21STomi Valkeinen } 301f7018c21STomi Valkeinen } 302f7018c21STomi Valkeinen } 303f7018c21STomi Valkeinen } 304f7018c21STomi Valkeinen 305f7018c21STomi Valkeinen static void clear_palette(struct pm2fb_par *p) 306f7018c21STomi Valkeinen { 307f7018c21STomi Valkeinen int i = 256; 308f7018c21STomi Valkeinen 309f7018c21STomi Valkeinen WAIT_FIFO(p, 1); 310f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, 0); 311f7018c21STomi Valkeinen wmb(); 312f7018c21STomi Valkeinen while (i--) { 313f7018c21STomi Valkeinen WAIT_FIFO(p, 3); 314f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_DATA, 0); 315f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_DATA, 0); 316f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_DATA, 0); 317f7018c21STomi Valkeinen } 318f7018c21STomi Valkeinen } 319f7018c21STomi Valkeinen 320f7018c21STomi Valkeinen static void reset_card(struct pm2fb_par *p) 321f7018c21STomi Valkeinen { 322f7018c21STomi Valkeinen if (p->type == PM2_TYPE_PERMEDIA2V) 323f7018c21STomi Valkeinen pm2_WR(p, PM2VR_RD_INDEX_HIGH, 0); 324f7018c21STomi Valkeinen pm2_WR(p, PM2R_RESET_STATUS, 0); 325f7018c21STomi Valkeinen mb(); 326f7018c21STomi Valkeinen while (pm2_RD(p, PM2R_RESET_STATUS) & PM2F_BEING_RESET) 327f7018c21STomi Valkeinen cpu_relax(); 328f7018c21STomi Valkeinen mb(); 329f7018c21STomi Valkeinen #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT 330f7018c21STomi Valkeinen DPRINTK("FIFO disconnect enabled\n"); 331f7018c21STomi Valkeinen pm2_WR(p, PM2R_FIFO_DISCON, 1); 332f7018c21STomi Valkeinen mb(); 333f7018c21STomi Valkeinen #endif 334f7018c21STomi Valkeinen 335f7018c21STomi Valkeinen /* Restore stashed memory config information from probe */ 336f7018c21STomi Valkeinen WAIT_FIFO(p, 3); 337f7018c21STomi Valkeinen pm2_WR(p, PM2R_MEM_CONTROL, p->mem_control); 338f7018c21STomi Valkeinen pm2_WR(p, PM2R_BOOT_ADDRESS, p->boot_address); 339f7018c21STomi Valkeinen wmb(); 340f7018c21STomi Valkeinen pm2_WR(p, PM2R_MEM_CONFIG, p->mem_config); 341f7018c21STomi Valkeinen } 342f7018c21STomi Valkeinen 343f7018c21STomi Valkeinen static void reset_config(struct pm2fb_par *p) 344f7018c21STomi Valkeinen { 345f7018c21STomi Valkeinen WAIT_FIFO(p, 53); 346f7018c21STomi Valkeinen pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG) & 347f7018c21STomi Valkeinen ~(PM2F_VGA_ENABLE | PM2F_VGA_FIXED)); 348f7018c21STomi Valkeinen pm2_WR(p, PM2R_BYPASS_WRITE_MASK, ~(0L)); 349f7018c21STomi Valkeinen pm2_WR(p, PM2R_FRAMEBUFFER_WRITE_MASK, ~(0L)); 350f7018c21STomi Valkeinen pm2_WR(p, PM2R_FIFO_CONTROL, 0); 351f7018c21STomi Valkeinen pm2_WR(p, PM2R_APERTURE_ONE, 0); 352f7018c21STomi Valkeinen pm2_WR(p, PM2R_APERTURE_TWO, 0); 353f7018c21STomi Valkeinen pm2_WR(p, PM2R_RASTERIZER_MODE, 0); 354f7018c21STomi Valkeinen pm2_WR(p, PM2R_DELTA_MODE, PM2F_DELTA_ORDER_RGB); 355f7018c21STomi Valkeinen pm2_WR(p, PM2R_LB_READ_FORMAT, 0); 356f7018c21STomi Valkeinen pm2_WR(p, PM2R_LB_WRITE_FORMAT, 0); 357f7018c21STomi Valkeinen pm2_WR(p, PM2R_LB_READ_MODE, 0); 358f7018c21STomi Valkeinen pm2_WR(p, PM2R_LB_SOURCE_OFFSET, 0); 359f7018c21STomi Valkeinen pm2_WR(p, PM2R_FB_SOURCE_OFFSET, 0); 360f7018c21STomi Valkeinen pm2_WR(p, PM2R_FB_PIXEL_OFFSET, 0); 361f7018c21STomi Valkeinen pm2_WR(p, PM2R_FB_WINDOW_BASE, 0); 362f7018c21STomi Valkeinen pm2_WR(p, PM2R_LB_WINDOW_BASE, 0); 363f7018c21STomi Valkeinen pm2_WR(p, PM2R_FB_SOFT_WRITE_MASK, ~(0L)); 364f7018c21STomi Valkeinen pm2_WR(p, PM2R_FB_HARD_WRITE_MASK, ~(0L)); 365f7018c21STomi Valkeinen pm2_WR(p, PM2R_FB_READ_PIXEL, 0); 366f7018c21STomi Valkeinen pm2_WR(p, PM2R_DITHER_MODE, 0); 367f7018c21STomi Valkeinen pm2_WR(p, PM2R_AREA_STIPPLE_MODE, 0); 368f7018c21STomi Valkeinen pm2_WR(p, PM2R_DEPTH_MODE, 0); 369f7018c21STomi Valkeinen pm2_WR(p, PM2R_STENCIL_MODE, 0); 370f7018c21STomi Valkeinen pm2_WR(p, PM2R_TEXTURE_ADDRESS_MODE, 0); 371f7018c21STomi Valkeinen pm2_WR(p, PM2R_TEXTURE_READ_MODE, 0); 372f7018c21STomi Valkeinen pm2_WR(p, PM2R_TEXEL_LUT_MODE, 0); 373f7018c21STomi Valkeinen pm2_WR(p, PM2R_YUV_MODE, 0); 374f7018c21STomi Valkeinen pm2_WR(p, PM2R_COLOR_DDA_MODE, 0); 375f7018c21STomi Valkeinen pm2_WR(p, PM2R_TEXTURE_COLOR_MODE, 0); 376f7018c21STomi Valkeinen pm2_WR(p, PM2R_FOG_MODE, 0); 377f7018c21STomi Valkeinen pm2_WR(p, PM2R_ALPHA_BLEND_MODE, 0); 378f7018c21STomi Valkeinen pm2_WR(p, PM2R_LOGICAL_OP_MODE, 0); 379f7018c21STomi Valkeinen pm2_WR(p, PM2R_STATISTICS_MODE, 0); 380f7018c21STomi Valkeinen pm2_WR(p, PM2R_SCISSOR_MODE, 0); 381f7018c21STomi Valkeinen pm2_WR(p, PM2R_FILTER_MODE, PM2F_SYNCHRONIZATION); 382f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PIXEL_MASK, 0xff); 383f7018c21STomi Valkeinen switch (p->type) { 384f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2: 385f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_MODE_CONTROL, 0); /* no overlay */ 386f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_CURSOR_CONTROL, 0); 387f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, PM2F_RD_PALETTE_WIDTH_8); 388f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_COLOR_KEY_CONTROL, 0); 389f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_OVERLAY_KEY, 0); 390f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_RED_KEY, 0); 391f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_GREEN_KEY, 0); 392f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_BLUE_KEY, 0); 393f7018c21STomi Valkeinen break; 394f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2V: 395f7018c21STomi Valkeinen pm2v_RDAC_WR(p, PM2VI_RD_MISC_CONTROL, 1); /* 8bit */ 396f7018c21STomi Valkeinen break; 397f7018c21STomi Valkeinen } 398f7018c21STomi Valkeinen } 399f7018c21STomi Valkeinen 400f7018c21STomi Valkeinen static void set_aperture(struct pm2fb_par *p, u32 depth) 401f7018c21STomi Valkeinen { 402f7018c21STomi Valkeinen /* 403f7018c21STomi Valkeinen * The hardware is little-endian. When used in big-endian 404f7018c21STomi Valkeinen * hosts, the on-chip aperture settings are used where 405f7018c21STomi Valkeinen * possible to translate from host to card byte order. 406f7018c21STomi Valkeinen */ 407f7018c21STomi Valkeinen WAIT_FIFO(p, 2); 408f7018c21STomi Valkeinen #ifdef __LITTLE_ENDIAN 409f7018c21STomi Valkeinen pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_STANDARD); 410f7018c21STomi Valkeinen #else 411f7018c21STomi Valkeinen switch (depth) { 412f7018c21STomi Valkeinen case 24: /* RGB->BGR */ 413f7018c21STomi Valkeinen /* 414f7018c21STomi Valkeinen * We can't use the aperture to translate host to 415f7018c21STomi Valkeinen * card byte order here, so we switch to BGR mode 416f7018c21STomi Valkeinen * in pm2fb_set_par(). 417f7018c21STomi Valkeinen */ 418f7018c21STomi Valkeinen case 8: /* B->B */ 419f7018c21STomi Valkeinen pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_STANDARD); 420f7018c21STomi Valkeinen break; 421f7018c21STomi Valkeinen case 16: /* HL->LH */ 422f7018c21STomi Valkeinen pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_HALFWORDSWAP); 423f7018c21STomi Valkeinen break; 424f7018c21STomi Valkeinen case 32: /* RGBA->ABGR */ 425f7018c21STomi Valkeinen pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_BYTESWAP); 426f7018c21STomi Valkeinen break; 427f7018c21STomi Valkeinen } 428f7018c21STomi Valkeinen #endif 429f7018c21STomi Valkeinen 430f7018c21STomi Valkeinen /* We don't use aperture two, so this may be superflous */ 431f7018c21STomi Valkeinen pm2_WR(p, PM2R_APERTURE_TWO, PM2F_APERTURE_STANDARD); 432f7018c21STomi Valkeinen } 433f7018c21STomi Valkeinen 434f7018c21STomi Valkeinen static void set_color(struct pm2fb_par *p, unsigned char regno, 435f7018c21STomi Valkeinen unsigned char r, unsigned char g, unsigned char b) 436f7018c21STomi Valkeinen { 437f7018c21STomi Valkeinen WAIT_FIFO(p, 4); 438f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, regno); 439f7018c21STomi Valkeinen wmb(); 440f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_DATA, r); 441f7018c21STomi Valkeinen wmb(); 442f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_DATA, g); 443f7018c21STomi Valkeinen wmb(); 444f7018c21STomi Valkeinen pm2_WR(p, PM2R_RD_PALETTE_DATA, b); 445f7018c21STomi Valkeinen } 446f7018c21STomi Valkeinen 447f7018c21STomi Valkeinen static void set_memclock(struct pm2fb_par *par, u32 clk) 448f7018c21STomi Valkeinen { 449f7018c21STomi Valkeinen int i; 450f7018c21STomi Valkeinen unsigned char m, n, p; 451f7018c21STomi Valkeinen 452f7018c21STomi Valkeinen switch (par->type) { 453f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2V: 454f7018c21STomi Valkeinen pm2v_mnp(clk/2, &m, &n, &p); 455f7018c21STomi Valkeinen WAIT_FIFO(par, 12); 456f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_MCLK_CONTROL >> 8); 457f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 0); 458f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_MCLK_PRESCALE, m); 459f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_MCLK_FEEDBACK, n); 460f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_MCLK_POSTSCALE, p); 461f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 1); 462f7018c21STomi Valkeinen rmb(); 463f7018c21STomi Valkeinen for (i = 256; i; i--) 464f7018c21STomi Valkeinen if (pm2v_RDAC_RD(par, PM2VI_RD_MCLK_CONTROL) & 2) 465f7018c21STomi Valkeinen break; 466f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0); 467f7018c21STomi Valkeinen break; 468f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2: 469f7018c21STomi Valkeinen pm2_mnp(clk, &m, &n, &p); 470f7018c21STomi Valkeinen WAIT_FIFO(par, 10); 471f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_3, 6); 472f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_1, m); 473f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_2, n); 474f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_3, 8|p); 475f7018c21STomi Valkeinen pm2_RDAC_RD(par, PM2I_RD_MEMORY_CLOCK_STATUS); 476f7018c21STomi Valkeinen rmb(); 477f7018c21STomi Valkeinen for (i = 256; i; i--) 478f7018c21STomi Valkeinen if (pm2_RD(par, PM2R_RD_INDEXED_DATA) & PM2F_PLL_LOCKED) 479f7018c21STomi Valkeinen break; 480f7018c21STomi Valkeinen break; 481f7018c21STomi Valkeinen } 482f7018c21STomi Valkeinen } 483f7018c21STomi Valkeinen 484f7018c21STomi Valkeinen static void set_pixclock(struct pm2fb_par *par, u32 clk) 485f7018c21STomi Valkeinen { 486f7018c21STomi Valkeinen int i; 487f7018c21STomi Valkeinen unsigned char m, n, p; 488f7018c21STomi Valkeinen 489f7018c21STomi Valkeinen switch (par->type) { 490f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2: 491f7018c21STomi Valkeinen pm2_mnp(clk, &m, &n, &p); 492f7018c21STomi Valkeinen WAIT_FIFO(par, 10); 493f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A3, 0); 494f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A1, m); 495f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A2, n); 496f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A3, 8|p); 497f7018c21STomi Valkeinen pm2_RDAC_RD(par, PM2I_RD_PIXEL_CLOCK_STATUS); 498f7018c21STomi Valkeinen rmb(); 499f7018c21STomi Valkeinen for (i = 256; i; i--) 500f7018c21STomi Valkeinen if (pm2_RD(par, PM2R_RD_INDEXED_DATA) & PM2F_PLL_LOCKED) 501f7018c21STomi Valkeinen break; 502f7018c21STomi Valkeinen break; 503f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2V: 504f7018c21STomi Valkeinen pm2v_mnp(clk/2, &m, &n, &p); 505f7018c21STomi Valkeinen WAIT_FIFO(par, 8); 506f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CLK0_PRESCALE >> 8); 507f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CLK0_PRESCALE, m); 508f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CLK0_FEEDBACK, n); 509f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CLK0_POSTSCALE, p); 510f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0); 511f7018c21STomi Valkeinen break; 512f7018c21STomi Valkeinen } 513f7018c21STomi Valkeinen } 514f7018c21STomi Valkeinen 515f7018c21STomi Valkeinen static void set_video(struct pm2fb_par *p, u32 video) 516f7018c21STomi Valkeinen { 517f7018c21STomi Valkeinen u32 tmp; 518f7018c21STomi Valkeinen u32 vsync = video; 519f7018c21STomi Valkeinen 520f7018c21STomi Valkeinen DPRINTK("video = 0x%x\n", video); 521f7018c21STomi Valkeinen 522f7018c21STomi Valkeinen /* 523f7018c21STomi Valkeinen * The hardware cursor needs +vsync to recognise vert retrace. 524f7018c21STomi Valkeinen * We may not be using the hardware cursor, but the X Glint 525f7018c21STomi Valkeinen * driver may well. So always set +hsync/+vsync and then set 526f7018c21STomi Valkeinen * the RAMDAC to invert the sync if necessary. 527f7018c21STomi Valkeinen */ 528f7018c21STomi Valkeinen vsync &= ~(PM2F_HSYNC_MASK | PM2F_VSYNC_MASK); 529f7018c21STomi Valkeinen vsync |= PM2F_HSYNC_ACT_HIGH | PM2F_VSYNC_ACT_HIGH; 530f7018c21STomi Valkeinen 531f7018c21STomi Valkeinen WAIT_FIFO(p, 3); 532f7018c21STomi Valkeinen pm2_WR(p, PM2R_VIDEO_CONTROL, vsync); 533f7018c21STomi Valkeinen 534f7018c21STomi Valkeinen switch (p->type) { 535f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2: 536f7018c21STomi Valkeinen tmp = PM2F_RD_PALETTE_WIDTH_8; 537f7018c21STomi Valkeinen if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW) 538f7018c21STomi Valkeinen tmp |= 4; /* invert hsync */ 539f7018c21STomi Valkeinen if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW) 540f7018c21STomi Valkeinen tmp |= 8; /* invert vsync */ 541f7018c21STomi Valkeinen pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, tmp); 542f7018c21STomi Valkeinen break; 543f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2V: 544f7018c21STomi Valkeinen tmp = 0; 545f7018c21STomi Valkeinen if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW) 546f7018c21STomi Valkeinen tmp |= 1; /* invert hsync */ 547f7018c21STomi Valkeinen if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW) 548f7018c21STomi Valkeinen tmp |= 4; /* invert vsync */ 549f7018c21STomi Valkeinen pm2v_RDAC_WR(p, PM2VI_RD_SYNC_CONTROL, tmp); 550f7018c21STomi Valkeinen break; 551f7018c21STomi Valkeinen } 552f7018c21STomi Valkeinen } 553f7018c21STomi Valkeinen 554f7018c21STomi Valkeinen /* 555f7018c21STomi Valkeinen * pm2fb_check_var - Optional function. Validates a var passed in. 556f7018c21STomi Valkeinen * @var: frame buffer variable screen structure 557f7018c21STomi Valkeinen * @info: frame buffer structure that represents a single frame buffer 558f7018c21STomi Valkeinen * 559f7018c21STomi Valkeinen * Checks to see if the hardware supports the state requested by 560f7018c21STomi Valkeinen * var passed in. 561f7018c21STomi Valkeinen * 562f7018c21STomi Valkeinen * Returns negative errno on error, or zero on success. 563f7018c21STomi Valkeinen */ 564f7018c21STomi Valkeinen static int pm2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 565f7018c21STomi Valkeinen { 566f7018c21STomi Valkeinen u32 lpitch; 567f7018c21STomi Valkeinen 568f7018c21STomi Valkeinen if (var->bits_per_pixel != 8 && var->bits_per_pixel != 16 && 569f7018c21STomi Valkeinen var->bits_per_pixel != 24 && var->bits_per_pixel != 32) { 570f7018c21STomi Valkeinen DPRINTK("depth not supported: %u\n", var->bits_per_pixel); 571f7018c21STomi Valkeinen return -EINVAL; 572f7018c21STomi Valkeinen } 573f7018c21STomi Valkeinen 574f7018c21STomi Valkeinen if (var->xres != var->xres_virtual) { 575f7018c21STomi Valkeinen DPRINTK("virtual x resolution != " 576f7018c21STomi Valkeinen "physical x resolution not supported\n"); 577f7018c21STomi Valkeinen return -EINVAL; 578f7018c21STomi Valkeinen } 579f7018c21STomi Valkeinen 580f7018c21STomi Valkeinen if (var->yres > var->yres_virtual) { 581f7018c21STomi Valkeinen DPRINTK("virtual y resolution < " 582f7018c21STomi Valkeinen "physical y resolution not possible\n"); 583f7018c21STomi Valkeinen return -EINVAL; 584f7018c21STomi Valkeinen } 585f7018c21STomi Valkeinen 586f7018c21STomi Valkeinen /* permedia cannot blit over 2048 */ 587f7018c21STomi Valkeinen if (var->yres_virtual > 2047) { 588f7018c21STomi Valkeinen var->yres_virtual = 2047; 589f7018c21STomi Valkeinen } 590f7018c21STomi Valkeinen 591f7018c21STomi Valkeinen if (var->xoffset) { 592f7018c21STomi Valkeinen DPRINTK("xoffset not supported\n"); 593f7018c21STomi Valkeinen return -EINVAL; 594f7018c21STomi Valkeinen } 595f7018c21STomi Valkeinen 596f7018c21STomi Valkeinen if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { 597f7018c21STomi Valkeinen DPRINTK("interlace not supported\n"); 598f7018c21STomi Valkeinen return -EINVAL; 599f7018c21STomi Valkeinen } 600f7018c21STomi Valkeinen 601f7018c21STomi Valkeinen var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */ 602f7018c21STomi Valkeinen lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3); 603f7018c21STomi Valkeinen 604f7018c21STomi Valkeinen if (var->xres < 320 || var->xres > 1600) { 605f7018c21STomi Valkeinen DPRINTK("width not supported: %u\n", var->xres); 606f7018c21STomi Valkeinen return -EINVAL; 607f7018c21STomi Valkeinen } 608f7018c21STomi Valkeinen 609f7018c21STomi Valkeinen if (var->yres < 200 || var->yres > 1200) { 610f7018c21STomi Valkeinen DPRINTK("height not supported: %u\n", var->yres); 611f7018c21STomi Valkeinen return -EINVAL; 612f7018c21STomi Valkeinen } 613f7018c21STomi Valkeinen 614f7018c21STomi Valkeinen if (lpitch * var->yres_virtual > info->fix.smem_len) { 615f7018c21STomi Valkeinen DPRINTK("no memory for screen (%ux%ux%u)\n", 616f7018c21STomi Valkeinen var->xres, var->yres_virtual, var->bits_per_pixel); 617f7018c21STomi Valkeinen return -EINVAL; 618f7018c21STomi Valkeinen } 619f7018c21STomi Valkeinen 620f7018c21STomi Valkeinen if (PICOS2KHZ(var->pixclock) > PM2_MAX_PIXCLOCK) { 621f7018c21STomi Valkeinen DPRINTK("pixclock too high (%ldKHz)\n", 622f7018c21STomi Valkeinen PICOS2KHZ(var->pixclock)); 623f7018c21STomi Valkeinen return -EINVAL; 624f7018c21STomi Valkeinen } 625f7018c21STomi Valkeinen 626f7018c21STomi Valkeinen var->transp.offset = 0; 627f7018c21STomi Valkeinen var->transp.length = 0; 628f7018c21STomi Valkeinen switch (var->bits_per_pixel) { 629f7018c21STomi Valkeinen case 8: 630f7018c21STomi Valkeinen var->red.length = 8; 631f7018c21STomi Valkeinen var->green.length = 8; 632f7018c21STomi Valkeinen var->blue.length = 8; 633f7018c21STomi Valkeinen break; 634f7018c21STomi Valkeinen case 16: 635f7018c21STomi Valkeinen var->red.offset = 11; 636f7018c21STomi Valkeinen var->red.length = 5; 637f7018c21STomi Valkeinen var->green.offset = 5; 638f7018c21STomi Valkeinen var->green.length = 6; 639f7018c21STomi Valkeinen var->blue.offset = 0; 640f7018c21STomi Valkeinen var->blue.length = 5; 641f7018c21STomi Valkeinen break; 642f7018c21STomi Valkeinen case 32: 643f7018c21STomi Valkeinen var->transp.offset = 24; 644f7018c21STomi Valkeinen var->transp.length = 8; 645f7018c21STomi Valkeinen var->red.offset = 16; 646f7018c21STomi Valkeinen var->green.offset = 8; 647f7018c21STomi Valkeinen var->blue.offset = 0; 648f7018c21STomi Valkeinen var->red.length = 8; 649f7018c21STomi Valkeinen var->green.length = 8; 650f7018c21STomi Valkeinen var->blue.length = 8; 651f7018c21STomi Valkeinen break; 652f7018c21STomi Valkeinen case 24: 653f7018c21STomi Valkeinen #ifdef __BIG_ENDIAN 654f7018c21STomi Valkeinen var->red.offset = 0; 655f7018c21STomi Valkeinen var->blue.offset = 16; 656f7018c21STomi Valkeinen #else 657f7018c21STomi Valkeinen var->red.offset = 16; 658f7018c21STomi Valkeinen var->blue.offset = 0; 659f7018c21STomi Valkeinen #endif 660f7018c21STomi Valkeinen var->green.offset = 8; 661f7018c21STomi Valkeinen var->red.length = 8; 662f7018c21STomi Valkeinen var->green.length = 8; 663f7018c21STomi Valkeinen var->blue.length = 8; 664f7018c21STomi Valkeinen break; 665f7018c21STomi Valkeinen } 666f7018c21STomi Valkeinen var->height = -1; 667f7018c21STomi Valkeinen var->width = -1; 668f7018c21STomi Valkeinen 669f7018c21STomi Valkeinen var->accel_flags = 0; /* Can't mmap if this is on */ 670f7018c21STomi Valkeinen 671f7018c21STomi Valkeinen DPRINTK("Checking graphics mode at %dx%d depth %d\n", 672f7018c21STomi Valkeinen var->xres, var->yres, var->bits_per_pixel); 673f7018c21STomi Valkeinen return 0; 674f7018c21STomi Valkeinen } 675f7018c21STomi Valkeinen 676f7018c21STomi Valkeinen /** 677f7018c21STomi Valkeinen * pm2fb_set_par - Alters the hardware state. 678f7018c21STomi Valkeinen * @info: frame buffer structure that represents a single frame buffer 679f7018c21STomi Valkeinen * 680f7018c21STomi Valkeinen * Using the fb_var_screeninfo in fb_info we set the resolution of the 681f7018c21STomi Valkeinen * this particular framebuffer. 682f7018c21STomi Valkeinen */ 683f7018c21STomi Valkeinen static int pm2fb_set_par(struct fb_info *info) 684f7018c21STomi Valkeinen { 685f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 686f7018c21STomi Valkeinen u32 pixclock; 687f7018c21STomi Valkeinen u32 width = (info->var.xres_virtual + 7) & ~7; 688f7018c21STomi Valkeinen u32 height = info->var.yres_virtual; 689f7018c21STomi Valkeinen u32 depth = (info->var.bits_per_pixel + 7) & ~7; 690f7018c21STomi Valkeinen u32 hsstart, hsend, hbend, htotal; 691f7018c21STomi Valkeinen u32 vsstart, vsend, vbend, vtotal; 692f7018c21STomi Valkeinen u32 stride; 693f7018c21STomi Valkeinen u32 base; 694f7018c21STomi Valkeinen u32 video = 0; 695f7018c21STomi Valkeinen u32 clrmode = PM2F_RD_COLOR_MODE_RGB | PM2F_RD_GUI_ACTIVE; 696f7018c21STomi Valkeinen u32 txtmap = 0; 697f7018c21STomi Valkeinen u32 pixsize = 0; 698f7018c21STomi Valkeinen u32 clrformat = 0; 699f7018c21STomi Valkeinen u32 misc = 1; /* 8-bit DAC */ 700f7018c21STomi Valkeinen u32 xres = (info->var.xres + 31) & ~31; 701f7018c21STomi Valkeinen int data64; 702f7018c21STomi Valkeinen 703f7018c21STomi Valkeinen reset_card(par); 704f7018c21STomi Valkeinen reset_config(par); 705f7018c21STomi Valkeinen clear_palette(par); 706f7018c21STomi Valkeinen if (par->memclock) 707f7018c21STomi Valkeinen set_memclock(par, par->memclock); 708f7018c21STomi Valkeinen 709f7018c21STomi Valkeinen depth = (depth > 32) ? 32 : depth; 710f7018c21STomi Valkeinen data64 = depth > 8 || par->type == PM2_TYPE_PERMEDIA2V; 711f7018c21STomi Valkeinen 712f7018c21STomi Valkeinen pixclock = PICOS2KHZ(info->var.pixclock); 713f7018c21STomi Valkeinen if (pixclock > PM2_MAX_PIXCLOCK) { 714f7018c21STomi Valkeinen DPRINTK("pixclock too high (%uKHz)\n", pixclock); 715f7018c21STomi Valkeinen return -EINVAL; 716f7018c21STomi Valkeinen } 717f7018c21STomi Valkeinen 718f7018c21STomi Valkeinen hsstart = to3264(info->var.right_margin, depth, data64); 719f7018c21STomi Valkeinen hsend = hsstart + to3264(info->var.hsync_len, depth, data64); 720f7018c21STomi Valkeinen hbend = hsend + to3264(info->var.left_margin, depth, data64); 721f7018c21STomi Valkeinen htotal = to3264(xres, depth, data64) + hbend - 1; 722f7018c21STomi Valkeinen vsstart = (info->var.lower_margin) 723f7018c21STomi Valkeinen ? info->var.lower_margin - 1 724f7018c21STomi Valkeinen : 0; /* FIXME! */ 725f7018c21STomi Valkeinen vsend = info->var.lower_margin + info->var.vsync_len - 1; 726f7018c21STomi Valkeinen vbend = info->var.lower_margin + info->var.vsync_len + 727f7018c21STomi Valkeinen info->var.upper_margin; 728f7018c21STomi Valkeinen vtotal = info->var.yres + vbend - 1; 729f7018c21STomi Valkeinen stride = to3264(width, depth, 1); 730f7018c21STomi Valkeinen base = to3264(info->var.yoffset * xres + info->var.xoffset, depth, 1); 731f7018c21STomi Valkeinen if (data64) 732f7018c21STomi Valkeinen video |= PM2F_DATA_64_ENABLE; 733f7018c21STomi Valkeinen 734f7018c21STomi Valkeinen if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) { 735f7018c21STomi Valkeinen if (lowhsync) { 736f7018c21STomi Valkeinen DPRINTK("ignoring +hsync, using -hsync.\n"); 737f7018c21STomi Valkeinen video |= PM2F_HSYNC_ACT_LOW; 738f7018c21STomi Valkeinen } else 739f7018c21STomi Valkeinen video |= PM2F_HSYNC_ACT_HIGH; 740f7018c21STomi Valkeinen } else 741f7018c21STomi Valkeinen video |= PM2F_HSYNC_ACT_LOW; 742f7018c21STomi Valkeinen 743f7018c21STomi Valkeinen if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) { 744f7018c21STomi Valkeinen if (lowvsync) { 745f7018c21STomi Valkeinen DPRINTK("ignoring +vsync, using -vsync.\n"); 746f7018c21STomi Valkeinen video |= PM2F_VSYNC_ACT_LOW; 747f7018c21STomi Valkeinen } else 748f7018c21STomi Valkeinen video |= PM2F_VSYNC_ACT_HIGH; 749f7018c21STomi Valkeinen } else 750f7018c21STomi Valkeinen video |= PM2F_VSYNC_ACT_LOW; 751f7018c21STomi Valkeinen 752f7018c21STomi Valkeinen if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { 753f7018c21STomi Valkeinen DPRINTK("interlaced not supported\n"); 754f7018c21STomi Valkeinen return -EINVAL; 755f7018c21STomi Valkeinen } 756f7018c21STomi Valkeinen if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) 757f7018c21STomi Valkeinen video |= PM2F_LINE_DOUBLE; 758f7018c21STomi Valkeinen if ((info->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) 759f7018c21STomi Valkeinen video |= PM2F_VIDEO_ENABLE; 760f7018c21STomi Valkeinen par->video = video; 761f7018c21STomi Valkeinen 762f7018c21STomi Valkeinen info->fix.visual = 763f7018c21STomi Valkeinen (depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 764f7018c21STomi Valkeinen info->fix.line_length = info->var.xres * depth / 8; 765f7018c21STomi Valkeinen info->cmap.len = 256; 766f7018c21STomi Valkeinen 767f7018c21STomi Valkeinen /* 768f7018c21STomi Valkeinen * Settings calculated. Now write them out. 769f7018c21STomi Valkeinen */ 770f7018c21STomi Valkeinen if (par->type == PM2_TYPE_PERMEDIA2V) { 771f7018c21STomi Valkeinen WAIT_FIFO(par, 1); 772f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0); 773f7018c21STomi Valkeinen } 774f7018c21STomi Valkeinen 775f7018c21STomi Valkeinen set_aperture(par, depth); 776f7018c21STomi Valkeinen 777f7018c21STomi Valkeinen mb(); 778f7018c21STomi Valkeinen WAIT_FIFO(par, 19); 779f7018c21STomi Valkeinen switch (depth) { 780f7018c21STomi Valkeinen case 8: 781f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_READ_PIXEL, 0); 782f7018c21STomi Valkeinen clrformat = 0x2e; 783f7018c21STomi Valkeinen break; 784f7018c21STomi Valkeinen case 16: 785f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_READ_PIXEL, 1); 786f7018c21STomi Valkeinen clrmode |= PM2F_RD_TRUECOLOR | PM2F_RD_PIXELFORMAT_RGB565; 787f7018c21STomi Valkeinen txtmap = PM2F_TEXTEL_SIZE_16; 788f7018c21STomi Valkeinen pixsize = 1; 789f7018c21STomi Valkeinen clrformat = 0x70; 790f7018c21STomi Valkeinen misc |= 8; 791f7018c21STomi Valkeinen break; 792f7018c21STomi Valkeinen case 32: 793f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_READ_PIXEL, 2); 794f7018c21STomi Valkeinen clrmode |= PM2F_RD_TRUECOLOR | PM2F_RD_PIXELFORMAT_RGBA8888; 795f7018c21STomi Valkeinen txtmap = PM2F_TEXTEL_SIZE_32; 796f7018c21STomi Valkeinen pixsize = 2; 797f7018c21STomi Valkeinen clrformat = 0x20; 798f7018c21STomi Valkeinen misc |= 8; 799f7018c21STomi Valkeinen break; 800f7018c21STomi Valkeinen case 24: 801f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_READ_PIXEL, 4); 802f7018c21STomi Valkeinen clrmode |= PM2F_RD_TRUECOLOR | PM2F_RD_PIXELFORMAT_RGB888; 803f7018c21STomi Valkeinen txtmap = PM2F_TEXTEL_SIZE_24; 804f7018c21STomi Valkeinen pixsize = 4; 805f7018c21STomi Valkeinen clrformat = 0x20; 806f7018c21STomi Valkeinen misc |= 8; 807f7018c21STomi Valkeinen break; 808f7018c21STomi Valkeinen } 809f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_WRITE_MODE, PM2F_FB_WRITE_ENABLE); 810f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_READ_MODE, partprod(xres)); 811f7018c21STomi Valkeinen pm2_WR(par, PM2R_LB_READ_MODE, partprod(xres)); 812f7018c21STomi Valkeinen pm2_WR(par, PM2R_TEXTURE_MAP_FORMAT, txtmap | partprod(xres)); 813f7018c21STomi Valkeinen pm2_WR(par, PM2R_H_TOTAL, htotal); 814f7018c21STomi Valkeinen pm2_WR(par, PM2R_HS_START, hsstart); 815f7018c21STomi Valkeinen pm2_WR(par, PM2R_HS_END, hsend); 816f7018c21STomi Valkeinen pm2_WR(par, PM2R_HG_END, hbend); 817f7018c21STomi Valkeinen pm2_WR(par, PM2R_HB_END, hbend); 818f7018c21STomi Valkeinen pm2_WR(par, PM2R_V_TOTAL, vtotal); 819f7018c21STomi Valkeinen pm2_WR(par, PM2R_VS_START, vsstart); 820f7018c21STomi Valkeinen pm2_WR(par, PM2R_VS_END, vsend); 821f7018c21STomi Valkeinen pm2_WR(par, PM2R_VB_END, vbend); 822f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCREEN_STRIDE, stride); 823f7018c21STomi Valkeinen wmb(); 824f7018c21STomi Valkeinen pm2_WR(par, PM2R_WINDOW_ORIGIN, 0); 825f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCREEN_SIZE, (height << 16) | width); 826f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCISSOR_MODE, PM2F_SCREEN_SCISSOR_ENABLE); 827f7018c21STomi Valkeinen wmb(); 828f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCREEN_BASE, base); 829f7018c21STomi Valkeinen wmb(); 830f7018c21STomi Valkeinen set_video(par, video); 831f7018c21STomi Valkeinen WAIT_FIFO(par, 10); 832f7018c21STomi Valkeinen switch (par->type) { 833f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2: 834f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_COLOR_MODE, clrmode); 835f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_COLOR_KEY_CONTROL, 836f7018c21STomi Valkeinen (depth == 8) ? 0 : PM2F_COLOR_KEY_TEST_OFF); 837f7018c21STomi Valkeinen break; 838f7018c21STomi Valkeinen case PM2_TYPE_PERMEDIA2V: 839f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_DAC_CONTROL, 0); 840f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_PIXEL_SIZE, pixsize); 841f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_COLOR_FORMAT, clrformat); 842f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_MISC_CONTROL, misc); 843f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_OVERLAY_KEY, 0); 844f7018c21STomi Valkeinen break; 845f7018c21STomi Valkeinen } 846f7018c21STomi Valkeinen set_pixclock(par, pixclock); 847f7018c21STomi Valkeinen DPRINTK("Setting graphics mode at %dx%d depth %d\n", 848f7018c21STomi Valkeinen info->var.xres, info->var.yres, info->var.bits_per_pixel); 849f7018c21STomi Valkeinen return 0; 850f7018c21STomi Valkeinen } 851f7018c21STomi Valkeinen 852f7018c21STomi Valkeinen /** 853f7018c21STomi Valkeinen * pm2fb_setcolreg - Sets a color register. 854f7018c21STomi Valkeinen * @regno: boolean, 0 copy local, 1 get_user() function 855f7018c21STomi Valkeinen * @red: frame buffer colormap structure 856f7018c21STomi Valkeinen * @green: The green value which can be up to 16 bits wide 857f7018c21STomi Valkeinen * @blue: The blue value which can be up to 16 bits wide. 858f7018c21STomi Valkeinen * @transp: If supported the alpha value which can be up to 16 bits wide. 859f7018c21STomi Valkeinen * @info: frame buffer info structure 860f7018c21STomi Valkeinen * 861f7018c21STomi Valkeinen * Set a single color register. The values supplied have a 16 bit 862f7018c21STomi Valkeinen * magnitude which needs to be scaled in this function for the hardware. 863f7018c21STomi Valkeinen * Pretty much a direct lift from tdfxfb.c. 864f7018c21STomi Valkeinen * 865f7018c21STomi Valkeinen * Returns negative errno on error, or zero on success. 866f7018c21STomi Valkeinen */ 867f7018c21STomi Valkeinen static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, 868f7018c21STomi Valkeinen unsigned blue, unsigned transp, 869f7018c21STomi Valkeinen struct fb_info *info) 870f7018c21STomi Valkeinen { 871f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 872f7018c21STomi Valkeinen 873f7018c21STomi Valkeinen if (regno >= info->cmap.len) /* no. of hw registers */ 874f7018c21STomi Valkeinen return -EINVAL; 875f7018c21STomi Valkeinen /* 876f7018c21STomi Valkeinen * Program hardware... do anything you want with transp 877f7018c21STomi Valkeinen */ 878f7018c21STomi Valkeinen 879f7018c21STomi Valkeinen /* grayscale works only partially under directcolor */ 880f7018c21STomi Valkeinen /* grayscale = 0.30*R + 0.59*G + 0.11*B */ 881f7018c21STomi Valkeinen if (info->var.grayscale) 882f7018c21STomi Valkeinen red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 883f7018c21STomi Valkeinen 884f7018c21STomi Valkeinen /* Directcolor: 885f7018c21STomi Valkeinen * var->{color}.offset contains start of bitfield 886f7018c21STomi Valkeinen * var->{color}.length contains length of bitfield 887f7018c21STomi Valkeinen * {hardwarespecific} contains width of DAC 888f7018c21STomi Valkeinen * cmap[X] is programmed to 889f7018c21STomi Valkeinen * (X << red.offset) | (X << green.offset) | (X << blue.offset) 890f7018c21STomi Valkeinen * RAMDAC[X] is programmed to (red, green, blue) 891f7018c21STomi Valkeinen * 892f7018c21STomi Valkeinen * Pseudocolor: 893f7018c21STomi Valkeinen * uses offset = 0 && length = DAC register width. 894f7018c21STomi Valkeinen * var->{color}.offset is 0 895f7018c21STomi Valkeinen * var->{color}.length contains width of DAC 896f7018c21STomi Valkeinen * cmap is not used 897f7018c21STomi Valkeinen * DAC[X] is programmed to (red, green, blue) 898f7018c21STomi Valkeinen * Truecolor: 899f7018c21STomi Valkeinen * does not use RAMDAC (usually has 3 of them). 900f7018c21STomi Valkeinen * var->{color}.offset contains start of bitfield 901f7018c21STomi Valkeinen * var->{color}.length contains length of bitfield 902f7018c21STomi Valkeinen * cmap is programmed to 903f7018c21STomi Valkeinen * (red << red.offset) | (green << green.offset) | 904f7018c21STomi Valkeinen * (blue << blue.offset) | (transp << transp.offset) 905f7018c21STomi Valkeinen * RAMDAC does not exist 906f7018c21STomi Valkeinen */ 907f7018c21STomi Valkeinen #define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF -(val)) >> 16) 908f7018c21STomi Valkeinen switch (info->fix.visual) { 909f7018c21STomi Valkeinen case FB_VISUAL_TRUECOLOR: 910f7018c21STomi Valkeinen case FB_VISUAL_PSEUDOCOLOR: 911f7018c21STomi Valkeinen red = CNVT_TOHW(red, info->var.red.length); 912f7018c21STomi Valkeinen green = CNVT_TOHW(green, info->var.green.length); 913f7018c21STomi Valkeinen blue = CNVT_TOHW(blue, info->var.blue.length); 914f7018c21STomi Valkeinen transp = CNVT_TOHW(transp, info->var.transp.length); 915f7018c21STomi Valkeinen break; 916f7018c21STomi Valkeinen case FB_VISUAL_DIRECTCOLOR: 917f7018c21STomi Valkeinen /* example here assumes 8 bit DAC. Might be different 918f7018c21STomi Valkeinen * for your hardware */ 919f7018c21STomi Valkeinen red = CNVT_TOHW(red, 8); 920f7018c21STomi Valkeinen green = CNVT_TOHW(green, 8); 921f7018c21STomi Valkeinen blue = CNVT_TOHW(blue, 8); 922f7018c21STomi Valkeinen /* hey, there is bug in transp handling... */ 923f7018c21STomi Valkeinen transp = CNVT_TOHW(transp, 8); 924f7018c21STomi Valkeinen break; 925f7018c21STomi Valkeinen } 926f7018c21STomi Valkeinen #undef CNVT_TOHW 927f7018c21STomi Valkeinen /* Truecolor has hardware independent palette */ 928f7018c21STomi Valkeinen if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 929f7018c21STomi Valkeinen u32 v; 930f7018c21STomi Valkeinen 931f7018c21STomi Valkeinen if (regno >= 16) 932f7018c21STomi Valkeinen return -EINVAL; 933f7018c21STomi Valkeinen 934f7018c21STomi Valkeinen v = (red << info->var.red.offset) | 935f7018c21STomi Valkeinen (green << info->var.green.offset) | 936f7018c21STomi Valkeinen (blue << info->var.blue.offset) | 937f7018c21STomi Valkeinen (transp << info->var.transp.offset); 938f7018c21STomi Valkeinen 939f7018c21STomi Valkeinen switch (info->var.bits_per_pixel) { 940f7018c21STomi Valkeinen case 8: 941f7018c21STomi Valkeinen break; 942f7018c21STomi Valkeinen case 16: 943f7018c21STomi Valkeinen case 24: 944f7018c21STomi Valkeinen case 32: 945f7018c21STomi Valkeinen par->palette[regno] = v; 946f7018c21STomi Valkeinen break; 947f7018c21STomi Valkeinen } 948f7018c21STomi Valkeinen return 0; 949f7018c21STomi Valkeinen } else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) 950f7018c21STomi Valkeinen set_color(par, regno, red, green, blue); 951f7018c21STomi Valkeinen 952f7018c21STomi Valkeinen return 0; 953f7018c21STomi Valkeinen } 954f7018c21STomi Valkeinen 955f7018c21STomi Valkeinen /** 956f7018c21STomi Valkeinen * pm2fb_pan_display - Pans the display. 957f7018c21STomi Valkeinen * @var: frame buffer variable screen structure 958f7018c21STomi Valkeinen * @info: frame buffer structure that represents a single frame buffer 959f7018c21STomi Valkeinen * 960f7018c21STomi Valkeinen * Pan (or wrap, depending on the `vmode' field) the display using the 961f7018c21STomi Valkeinen * `xoffset' and `yoffset' fields of the `var' structure. 962f7018c21STomi Valkeinen * If the values don't fit, return -EINVAL. 963f7018c21STomi Valkeinen * 964f7018c21STomi Valkeinen * Returns negative errno on error, or zero on success. 965f7018c21STomi Valkeinen * 966f7018c21STomi Valkeinen */ 967f7018c21STomi Valkeinen static int pm2fb_pan_display(struct fb_var_screeninfo *var, 968f7018c21STomi Valkeinen struct fb_info *info) 969f7018c21STomi Valkeinen { 970f7018c21STomi Valkeinen struct pm2fb_par *p = info->par; 971f7018c21STomi Valkeinen u32 base; 972f7018c21STomi Valkeinen u32 depth = (info->var.bits_per_pixel + 7) & ~7; 973f7018c21STomi Valkeinen u32 xres = (info->var.xres + 31) & ~31; 974f7018c21STomi Valkeinen 975f7018c21STomi Valkeinen depth = (depth > 32) ? 32 : depth; 976f7018c21STomi Valkeinen base = to3264(var->yoffset * xres + var->xoffset, depth, 1); 977f7018c21STomi Valkeinen WAIT_FIFO(p, 1); 978f7018c21STomi Valkeinen pm2_WR(p, PM2R_SCREEN_BASE, base); 979f7018c21STomi Valkeinen return 0; 980f7018c21STomi Valkeinen } 981f7018c21STomi Valkeinen 982f7018c21STomi Valkeinen /** 983f7018c21STomi Valkeinen * pm2fb_blank - Blanks the display. 984f7018c21STomi Valkeinen * @blank_mode: the blank mode we want. 985f7018c21STomi Valkeinen * @info: frame buffer structure that represents a single frame buffer 986f7018c21STomi Valkeinen * 987f7018c21STomi Valkeinen * Blank the screen if blank_mode != 0, else unblank. Return 0 if 988f7018c21STomi Valkeinen * blanking succeeded, != 0 if un-/blanking failed due to e.g. a 989f7018c21STomi Valkeinen * video mode which doesn't support it. Implements VESA suspend 990f7018c21STomi Valkeinen * and powerdown modes on hardware that supports disabling hsync/vsync: 991f7018c21STomi Valkeinen * blank_mode == 2: suspend vsync 992f7018c21STomi Valkeinen * blank_mode == 3: suspend hsync 993f7018c21STomi Valkeinen * blank_mode == 4: powerdown 994f7018c21STomi Valkeinen * 995f7018c21STomi Valkeinen * Returns negative errno on error, or zero on success. 996f7018c21STomi Valkeinen * 997f7018c21STomi Valkeinen */ 998f7018c21STomi Valkeinen static int pm2fb_blank(int blank_mode, struct fb_info *info) 999f7018c21STomi Valkeinen { 1000f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1001f7018c21STomi Valkeinen u32 video = par->video; 1002f7018c21STomi Valkeinen 1003f7018c21STomi Valkeinen DPRINTK("blank_mode %d\n", blank_mode); 1004f7018c21STomi Valkeinen 1005f7018c21STomi Valkeinen switch (blank_mode) { 1006f7018c21STomi Valkeinen case FB_BLANK_UNBLANK: 1007f7018c21STomi Valkeinen /* Screen: On */ 1008f7018c21STomi Valkeinen video |= PM2F_VIDEO_ENABLE; 1009f7018c21STomi Valkeinen break; 1010f7018c21STomi Valkeinen case FB_BLANK_NORMAL: 1011f7018c21STomi Valkeinen /* Screen: Off */ 1012f7018c21STomi Valkeinen video &= ~PM2F_VIDEO_ENABLE; 1013f7018c21STomi Valkeinen break; 1014f7018c21STomi Valkeinen case FB_BLANK_VSYNC_SUSPEND: 1015f7018c21STomi Valkeinen /* VSync: Off */ 1016f7018c21STomi Valkeinen video &= ~(PM2F_VSYNC_MASK | PM2F_BLANK_LOW); 1017f7018c21STomi Valkeinen break; 1018f7018c21STomi Valkeinen case FB_BLANK_HSYNC_SUSPEND: 1019f7018c21STomi Valkeinen /* HSync: Off */ 1020f7018c21STomi Valkeinen video &= ~(PM2F_HSYNC_MASK | PM2F_BLANK_LOW); 1021f7018c21STomi Valkeinen break; 1022f7018c21STomi Valkeinen case FB_BLANK_POWERDOWN: 1023f7018c21STomi Valkeinen /* HSync: Off, VSync: Off */ 1024f7018c21STomi Valkeinen video &= ~(PM2F_VSYNC_MASK | PM2F_HSYNC_MASK | PM2F_BLANK_LOW); 1025f7018c21STomi Valkeinen break; 1026f7018c21STomi Valkeinen } 1027f7018c21STomi Valkeinen set_video(par, video); 1028f7018c21STomi Valkeinen return 0; 1029f7018c21STomi Valkeinen } 1030f7018c21STomi Valkeinen 1031f7018c21STomi Valkeinen static int pm2fb_sync(struct fb_info *info) 1032f7018c21STomi Valkeinen { 1033f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1034f7018c21STomi Valkeinen 1035f7018c21STomi Valkeinen WAIT_FIFO(par, 1); 1036f7018c21STomi Valkeinen pm2_WR(par, PM2R_SYNC, 0); 1037f7018c21STomi Valkeinen mb(); 1038f7018c21STomi Valkeinen do { 1039f7018c21STomi Valkeinen while (pm2_RD(par, PM2R_OUT_FIFO_WORDS) == 0) 1040f7018c21STomi Valkeinen cpu_relax(); 1041f7018c21STomi Valkeinen } while (pm2_RD(par, PM2R_OUT_FIFO) != PM2TAG(PM2R_SYNC)); 1042f7018c21STomi Valkeinen 1043f7018c21STomi Valkeinen return 0; 1044f7018c21STomi Valkeinen } 1045f7018c21STomi Valkeinen 1046f7018c21STomi Valkeinen static void pm2fb_fillrect(struct fb_info *info, 1047f7018c21STomi Valkeinen const struct fb_fillrect *region) 1048f7018c21STomi Valkeinen { 1049f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1050f7018c21STomi Valkeinen struct fb_fillrect modded; 1051f7018c21STomi Valkeinen int vxres, vyres; 1052f7018c21STomi Valkeinen u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ? 1053f7018c21STomi Valkeinen ((u32 *)info->pseudo_palette)[region->color] : region->color; 1054f7018c21STomi Valkeinen 1055f7018c21STomi Valkeinen if (info->state != FBINFO_STATE_RUNNING) 1056f7018c21STomi Valkeinen return; 1057f7018c21STomi Valkeinen if ((info->flags & FBINFO_HWACCEL_DISABLED) || 1058f7018c21STomi Valkeinen region->rop != ROP_COPY ) { 1059f7018c21STomi Valkeinen cfb_fillrect(info, region); 1060f7018c21STomi Valkeinen return; 1061f7018c21STomi Valkeinen } 1062f7018c21STomi Valkeinen 1063f7018c21STomi Valkeinen vxres = info->var.xres_virtual; 1064f7018c21STomi Valkeinen vyres = info->var.yres_virtual; 1065f7018c21STomi Valkeinen 1066f7018c21STomi Valkeinen memcpy(&modded, region, sizeof(struct fb_fillrect)); 1067f7018c21STomi Valkeinen 1068f7018c21STomi Valkeinen if (!modded.width || !modded.height || 1069f7018c21STomi Valkeinen modded.dx >= vxres || modded.dy >= vyres) 1070f7018c21STomi Valkeinen return; 1071f7018c21STomi Valkeinen 1072f7018c21STomi Valkeinen if (modded.dx + modded.width > vxres) 1073f7018c21STomi Valkeinen modded.width = vxres - modded.dx; 1074f7018c21STomi Valkeinen if (modded.dy + modded.height > vyres) 1075f7018c21STomi Valkeinen modded.height = vyres - modded.dy; 1076f7018c21STomi Valkeinen 1077f7018c21STomi Valkeinen if (info->var.bits_per_pixel == 8) 1078f7018c21STomi Valkeinen color |= color << 8; 1079f7018c21STomi Valkeinen if (info->var.bits_per_pixel <= 16) 1080f7018c21STomi Valkeinen color |= color << 16; 1081f7018c21STomi Valkeinen 1082f7018c21STomi Valkeinen WAIT_FIFO(par, 3); 1083f7018c21STomi Valkeinen pm2_WR(par, PM2R_CONFIG, PM2F_CONFIG_FB_WRITE_ENABLE); 1084f7018c21STomi Valkeinen pm2_WR(par, PM2R_RECTANGLE_ORIGIN, (modded.dy << 16) | modded.dx); 1085f7018c21STomi Valkeinen pm2_WR(par, PM2R_RECTANGLE_SIZE, (modded.height << 16) | modded.width); 1086f7018c21STomi Valkeinen if (info->var.bits_per_pixel != 24) { 1087f7018c21STomi Valkeinen WAIT_FIFO(par, 2); 1088f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_BLOCK_COLOR, color); 1089f7018c21STomi Valkeinen wmb(); 1090f7018c21STomi Valkeinen pm2_WR(par, PM2R_RENDER, 1091f7018c21STomi Valkeinen PM2F_RENDER_RECTANGLE | PM2F_RENDER_FASTFILL); 1092f7018c21STomi Valkeinen } else { 1093f7018c21STomi Valkeinen WAIT_FIFO(par, 4); 1094f7018c21STomi Valkeinen pm2_WR(par, PM2R_COLOR_DDA_MODE, 1); 1095f7018c21STomi Valkeinen pm2_WR(par, PM2R_CONSTANT_COLOR, color); 1096f7018c21STomi Valkeinen wmb(); 1097f7018c21STomi Valkeinen pm2_WR(par, PM2R_RENDER, 1098f7018c21STomi Valkeinen PM2F_RENDER_RECTANGLE | 1099f7018c21STomi Valkeinen PM2F_INCREASE_X | PM2F_INCREASE_Y ); 1100f7018c21STomi Valkeinen pm2_WR(par, PM2R_COLOR_DDA_MODE, 0); 1101f7018c21STomi Valkeinen } 1102f7018c21STomi Valkeinen } 1103f7018c21STomi Valkeinen 1104f7018c21STomi Valkeinen static void pm2fb_copyarea(struct fb_info *info, 1105f7018c21STomi Valkeinen const struct fb_copyarea *area) 1106f7018c21STomi Valkeinen { 1107f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1108f7018c21STomi Valkeinen struct fb_copyarea modded; 1109f7018c21STomi Valkeinen u32 vxres, vyres; 1110f7018c21STomi Valkeinen 1111f7018c21STomi Valkeinen if (info->state != FBINFO_STATE_RUNNING) 1112f7018c21STomi Valkeinen return; 1113f7018c21STomi Valkeinen if (info->flags & FBINFO_HWACCEL_DISABLED) { 1114f7018c21STomi Valkeinen cfb_copyarea(info, area); 1115f7018c21STomi Valkeinen return; 1116f7018c21STomi Valkeinen } 1117f7018c21STomi Valkeinen 1118f7018c21STomi Valkeinen memcpy(&modded, area, sizeof(struct fb_copyarea)); 1119f7018c21STomi Valkeinen 1120f7018c21STomi Valkeinen vxres = info->var.xres_virtual; 1121f7018c21STomi Valkeinen vyres = info->var.yres_virtual; 1122f7018c21STomi Valkeinen 1123f7018c21STomi Valkeinen if (!modded.width || !modded.height || 1124f7018c21STomi Valkeinen modded.sx >= vxres || modded.sy >= vyres || 1125f7018c21STomi Valkeinen modded.dx >= vxres || modded.dy >= vyres) 1126f7018c21STomi Valkeinen return; 1127f7018c21STomi Valkeinen 1128f7018c21STomi Valkeinen if (modded.sx + modded.width > vxres) 1129f7018c21STomi Valkeinen modded.width = vxres - modded.sx; 1130f7018c21STomi Valkeinen if (modded.dx + modded.width > vxres) 1131f7018c21STomi Valkeinen modded.width = vxres - modded.dx; 1132f7018c21STomi Valkeinen if (modded.sy + modded.height > vyres) 1133f7018c21STomi Valkeinen modded.height = vyres - modded.sy; 1134f7018c21STomi Valkeinen if (modded.dy + modded.height > vyres) 1135f7018c21STomi Valkeinen modded.height = vyres - modded.dy; 1136f7018c21STomi Valkeinen 1137f7018c21STomi Valkeinen WAIT_FIFO(par, 5); 1138f7018c21STomi Valkeinen pm2_WR(par, PM2R_CONFIG, PM2F_CONFIG_FB_WRITE_ENABLE | 1139f7018c21STomi Valkeinen PM2F_CONFIG_FB_READ_SOURCE_ENABLE); 1140f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_SOURCE_DELTA, 1141f7018c21STomi Valkeinen ((modded.sy - modded.dy) & 0xfff) << 16 | 1142f7018c21STomi Valkeinen ((modded.sx - modded.dx) & 0xfff)); 1143f7018c21STomi Valkeinen pm2_WR(par, PM2R_RECTANGLE_ORIGIN, (modded.dy << 16) | modded.dx); 1144f7018c21STomi Valkeinen pm2_WR(par, PM2R_RECTANGLE_SIZE, (modded.height << 16) | modded.width); 1145f7018c21STomi Valkeinen wmb(); 1146f7018c21STomi Valkeinen pm2_WR(par, PM2R_RENDER, PM2F_RENDER_RECTANGLE | 1147f7018c21STomi Valkeinen (modded.dx < modded.sx ? PM2F_INCREASE_X : 0) | 1148f7018c21STomi Valkeinen (modded.dy < modded.sy ? PM2F_INCREASE_Y : 0)); 1149f7018c21STomi Valkeinen } 1150f7018c21STomi Valkeinen 1151f7018c21STomi Valkeinen static void pm2fb_imageblit(struct fb_info *info, const struct fb_image *image) 1152f7018c21STomi Valkeinen { 1153f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1154f7018c21STomi Valkeinen u32 height = image->height; 1155f7018c21STomi Valkeinen u32 fgx, bgx; 1156f7018c21STomi Valkeinen const u32 *src = (const u32 *)image->data; 1157f7018c21STomi Valkeinen u32 xres = (info->var.xres + 31) & ~31; 1158f7018c21STomi Valkeinen int raster_mode = 1; /* invert bits */ 1159f7018c21STomi Valkeinen 1160f7018c21STomi Valkeinen #ifdef __LITTLE_ENDIAN 1161f7018c21STomi Valkeinen raster_mode |= 3 << 7; /* reverse byte order */ 1162f7018c21STomi Valkeinen #endif 1163f7018c21STomi Valkeinen 1164f7018c21STomi Valkeinen if (info->state != FBINFO_STATE_RUNNING) 1165f7018c21STomi Valkeinen return; 1166f7018c21STomi Valkeinen if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1) { 1167f7018c21STomi Valkeinen cfb_imageblit(info, image); 1168f7018c21STomi Valkeinen return; 1169f7018c21STomi Valkeinen } 1170f7018c21STomi Valkeinen switch (info->fix.visual) { 1171f7018c21STomi Valkeinen case FB_VISUAL_PSEUDOCOLOR: 1172f7018c21STomi Valkeinen fgx = image->fg_color; 1173f7018c21STomi Valkeinen bgx = image->bg_color; 1174f7018c21STomi Valkeinen break; 1175f7018c21STomi Valkeinen case FB_VISUAL_TRUECOLOR: 1176f7018c21STomi Valkeinen default: 1177f7018c21STomi Valkeinen fgx = par->palette[image->fg_color]; 1178f7018c21STomi Valkeinen bgx = par->palette[image->bg_color]; 1179f7018c21STomi Valkeinen break; 1180f7018c21STomi Valkeinen } 1181f7018c21STomi Valkeinen if (info->var.bits_per_pixel == 8) { 1182f7018c21STomi Valkeinen fgx |= fgx << 8; 1183f7018c21STomi Valkeinen bgx |= bgx << 8; 1184f7018c21STomi Valkeinen } 1185f7018c21STomi Valkeinen if (info->var.bits_per_pixel <= 16) { 1186f7018c21STomi Valkeinen fgx |= fgx << 16; 1187f7018c21STomi Valkeinen bgx |= bgx << 16; 1188f7018c21STomi Valkeinen } 1189f7018c21STomi Valkeinen 1190f7018c21STomi Valkeinen WAIT_FIFO(par, 13); 1191f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_READ_MODE, partprod(xres)); 1192f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCISSOR_MIN_XY, 1193f7018c21STomi Valkeinen ((image->dy & 0xfff) << 16) | (image->dx & 0x0fff)); 1194f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCISSOR_MAX_XY, 1195f7018c21STomi Valkeinen (((image->dy + image->height) & 0x0fff) << 16) | 1196f7018c21STomi Valkeinen ((image->dx + image->width) & 0x0fff)); 1197f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCISSOR_MODE, 1); 1198f7018c21STomi Valkeinen /* GXcopy & UNIT_ENABLE */ 1199f7018c21STomi Valkeinen pm2_WR(par, PM2R_LOGICAL_OP_MODE, (0x3 << 1) | 1); 1200f7018c21STomi Valkeinen pm2_WR(par, PM2R_RECTANGLE_ORIGIN, 1201f7018c21STomi Valkeinen ((image->dy & 0xfff) << 16) | (image->dx & 0x0fff)); 1202f7018c21STomi Valkeinen pm2_WR(par, PM2R_RECTANGLE_SIZE, 1203f7018c21STomi Valkeinen ((image->height & 0x0fff) << 16) | 1204f7018c21STomi Valkeinen ((image->width) & 0x0fff)); 1205f7018c21STomi Valkeinen if (info->var.bits_per_pixel == 24) { 1206f7018c21STomi Valkeinen pm2_WR(par, PM2R_COLOR_DDA_MODE, 1); 1207f7018c21STomi Valkeinen /* clear area */ 1208f7018c21STomi Valkeinen pm2_WR(par, PM2R_CONSTANT_COLOR, bgx); 1209f7018c21STomi Valkeinen pm2_WR(par, PM2R_RENDER, 1210f7018c21STomi Valkeinen PM2F_RENDER_RECTANGLE | 1211f7018c21STomi Valkeinen PM2F_INCREASE_X | PM2F_INCREASE_Y); 1212f7018c21STomi Valkeinen /* BitMapPackEachScanline */ 1213f7018c21STomi Valkeinen pm2_WR(par, PM2R_RASTERIZER_MODE, raster_mode | (1 << 9)); 1214f7018c21STomi Valkeinen pm2_WR(par, PM2R_CONSTANT_COLOR, fgx); 1215f7018c21STomi Valkeinen pm2_WR(par, PM2R_RENDER, 1216f7018c21STomi Valkeinen PM2F_RENDER_RECTANGLE | 1217f7018c21STomi Valkeinen PM2F_INCREASE_X | PM2F_INCREASE_Y | 1218f7018c21STomi Valkeinen PM2F_RENDER_SYNC_ON_BIT_MASK); 1219f7018c21STomi Valkeinen } else { 1220f7018c21STomi Valkeinen pm2_WR(par, PM2R_COLOR_DDA_MODE, 0); 1221f7018c21STomi Valkeinen /* clear area */ 1222f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_BLOCK_COLOR, bgx); 1223f7018c21STomi Valkeinen pm2_WR(par, PM2R_RENDER, 1224f7018c21STomi Valkeinen PM2F_RENDER_RECTANGLE | 1225f7018c21STomi Valkeinen PM2F_RENDER_FASTFILL | 1226f7018c21STomi Valkeinen PM2F_INCREASE_X | PM2F_INCREASE_Y); 1227f7018c21STomi Valkeinen pm2_WR(par, PM2R_RASTERIZER_MODE, raster_mode); 1228f7018c21STomi Valkeinen pm2_WR(par, PM2R_FB_BLOCK_COLOR, fgx); 1229f7018c21STomi Valkeinen pm2_WR(par, PM2R_RENDER, 1230f7018c21STomi Valkeinen PM2F_RENDER_RECTANGLE | 1231f7018c21STomi Valkeinen PM2F_INCREASE_X | PM2F_INCREASE_Y | 1232f7018c21STomi Valkeinen PM2F_RENDER_FASTFILL | 1233f7018c21STomi Valkeinen PM2F_RENDER_SYNC_ON_BIT_MASK); 1234f7018c21STomi Valkeinen } 1235f7018c21STomi Valkeinen 1236f7018c21STomi Valkeinen while (height--) { 1237f7018c21STomi Valkeinen int width = ((image->width + 7) >> 3) 1238f7018c21STomi Valkeinen + info->pixmap.scan_align - 1; 1239f7018c21STomi Valkeinen width >>= 2; 1240f7018c21STomi Valkeinen WAIT_FIFO(par, width); 1241f7018c21STomi Valkeinen while (width--) { 1242f7018c21STomi Valkeinen pm2_WR(par, PM2R_BIT_MASK_PATTERN, *src); 1243f7018c21STomi Valkeinen src++; 1244f7018c21STomi Valkeinen } 1245f7018c21STomi Valkeinen } 1246f7018c21STomi Valkeinen WAIT_FIFO(par, 3); 1247f7018c21STomi Valkeinen pm2_WR(par, PM2R_RASTERIZER_MODE, 0); 1248f7018c21STomi Valkeinen pm2_WR(par, PM2R_COLOR_DDA_MODE, 0); 1249f7018c21STomi Valkeinen pm2_WR(par, PM2R_SCISSOR_MODE, 0); 1250f7018c21STomi Valkeinen } 1251f7018c21STomi Valkeinen 1252f7018c21STomi Valkeinen /* 1253f7018c21STomi Valkeinen * Hardware cursor support. 1254f7018c21STomi Valkeinen */ 1255f7018c21STomi Valkeinen static const u8 cursor_bits_lookup[16] = { 1256f7018c21STomi Valkeinen 0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54, 1257f7018c21STomi Valkeinen 0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55 1258f7018c21STomi Valkeinen }; 1259f7018c21STomi Valkeinen 1260f7018c21STomi Valkeinen static int pm2vfb_cursor(struct fb_info *info, struct fb_cursor *cursor) 1261f7018c21STomi Valkeinen { 1262f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1263f7018c21STomi Valkeinen u8 mode = PM2F_CURSORMODE_TYPE_X; 1264f7018c21STomi Valkeinen int x = cursor->image.dx - info->var.xoffset; 1265f7018c21STomi Valkeinen int y = cursor->image.dy - info->var.yoffset; 1266f7018c21STomi Valkeinen 1267f7018c21STomi Valkeinen if (cursor->enable) 1268f7018c21STomi Valkeinen mode |= PM2F_CURSORMODE_CURSOR_ENABLE; 1269f7018c21STomi Valkeinen 1270f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_MODE, mode); 1271f7018c21STomi Valkeinen 1272f7018c21STomi Valkeinen if (!cursor->enable) 1273f7018c21STomi Valkeinen x = 2047; /* push it outside display */ 1274f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_X_LOW, x & 0xff); 1275f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_X_HIGH, (x >> 8) & 0xf); 1276f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_Y_LOW, y & 0xff); 1277f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_Y_HIGH, (y >> 8) & 0xf); 1278f7018c21STomi Valkeinen 1279f7018c21STomi Valkeinen /* 1280f7018c21STomi Valkeinen * If the cursor is not be changed this means either we want the 1281f7018c21STomi Valkeinen * current cursor state (if enable is set) or we want to query what 1282f7018c21STomi Valkeinen * we can do with the cursor (if enable is not set) 1283f7018c21STomi Valkeinen */ 1284f7018c21STomi Valkeinen if (!cursor->set) 1285f7018c21STomi Valkeinen return 0; 1286f7018c21STomi Valkeinen 1287f7018c21STomi Valkeinen if (cursor->set & FB_CUR_SETHOT) { 1288f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_X_HOT, 1289f7018c21STomi Valkeinen cursor->hot.x & 0x3f); 1290f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_Y_HOT, 1291f7018c21STomi Valkeinen cursor->hot.y & 0x3f); 1292f7018c21STomi Valkeinen } 1293f7018c21STomi Valkeinen 1294f7018c21STomi Valkeinen if (cursor->set & FB_CUR_SETCMAP) { 1295f7018c21STomi Valkeinen u32 fg_idx = cursor->image.fg_color; 1296f7018c21STomi Valkeinen u32 bg_idx = cursor->image.bg_color; 1297f7018c21STomi Valkeinen struct fb_cmap cmap = info->cmap; 1298f7018c21STomi Valkeinen 1299f7018c21STomi Valkeinen /* the X11 driver says one should use these color registers */ 1300f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PALETTE >> 8); 1301f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_PALETTE + 0, 1302f7018c21STomi Valkeinen cmap.red[bg_idx] >> 8 ); 1303f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_PALETTE + 1, 1304f7018c21STomi Valkeinen cmap.green[bg_idx] >> 8 ); 1305f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_PALETTE + 2, 1306f7018c21STomi Valkeinen cmap.blue[bg_idx] >> 8 ); 1307f7018c21STomi Valkeinen 1308f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_PALETTE + 3, 1309f7018c21STomi Valkeinen cmap.red[fg_idx] >> 8 ); 1310f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_PALETTE + 4, 1311f7018c21STomi Valkeinen cmap.green[fg_idx] >> 8 ); 1312f7018c21STomi Valkeinen pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_PALETTE + 5, 1313f7018c21STomi Valkeinen cmap.blue[fg_idx] >> 8 ); 1314f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0); 1315f7018c21STomi Valkeinen } 1316f7018c21STomi Valkeinen 1317f7018c21STomi Valkeinen if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) { 1318f7018c21STomi Valkeinen u8 *bitmap = (u8 *)cursor->image.data; 1319f7018c21STomi Valkeinen u8 *mask = (u8 *)cursor->mask; 1320f7018c21STomi Valkeinen int i; 1321f7018c21STomi Valkeinen int pos = PM2VI_RD_CURSOR_PATTERN; 1322f7018c21STomi Valkeinen 1323f7018c21STomi Valkeinen for (i = 0; i < cursor->image.height; i++) { 1324f7018c21STomi Valkeinen int j = (cursor->image.width + 7) >> 3; 1325f7018c21STomi Valkeinen int k = 8 - j; 1326f7018c21STomi Valkeinen 1327f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, pos >> 8); 1328f7018c21STomi Valkeinen 1329f7018c21STomi Valkeinen for (; j > 0; j--) { 1330f7018c21STomi Valkeinen u8 data = *bitmap ^ *mask; 1331f7018c21STomi Valkeinen 1332f7018c21STomi Valkeinen if (cursor->rop == ROP_COPY) 1333f7018c21STomi Valkeinen data = *mask & *bitmap; 1334f7018c21STomi Valkeinen /* Upper 4 bits of bitmap data */ 1335f7018c21STomi Valkeinen pm2v_RDAC_WR(par, pos++, 1336f7018c21STomi Valkeinen cursor_bits_lookup[data >> 4] | 1337f7018c21STomi Valkeinen (cursor_bits_lookup[*mask >> 4] << 1)); 1338f7018c21STomi Valkeinen /* Lower 4 bits of bitmap */ 1339f7018c21STomi Valkeinen pm2v_RDAC_WR(par, pos++, 1340f7018c21STomi Valkeinen cursor_bits_lookup[data & 0xf] | 1341f7018c21STomi Valkeinen (cursor_bits_lookup[*mask & 0xf] << 1)); 1342f7018c21STomi Valkeinen bitmap++; 1343f7018c21STomi Valkeinen mask++; 1344f7018c21STomi Valkeinen } 1345f7018c21STomi Valkeinen for (; k > 0; k--) { 1346f7018c21STomi Valkeinen pm2v_RDAC_WR(par, pos++, 0); 1347f7018c21STomi Valkeinen pm2v_RDAC_WR(par, pos++, 0); 1348f7018c21STomi Valkeinen } 1349f7018c21STomi Valkeinen } 1350f7018c21STomi Valkeinen 1351f7018c21STomi Valkeinen while (pos < (1024 + PM2VI_RD_CURSOR_PATTERN)) { 1352f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, pos >> 8); 1353f7018c21STomi Valkeinen pm2v_RDAC_WR(par, pos++, 0); 1354f7018c21STomi Valkeinen } 1355f7018c21STomi Valkeinen 1356f7018c21STomi Valkeinen pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0); 1357f7018c21STomi Valkeinen } 1358f7018c21STomi Valkeinen return 0; 1359f7018c21STomi Valkeinen } 1360f7018c21STomi Valkeinen 1361f7018c21STomi Valkeinen static int pm2fb_cursor(struct fb_info *info, struct fb_cursor *cursor) 1362f7018c21STomi Valkeinen { 1363f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1364f7018c21STomi Valkeinen u8 mode; 1365f7018c21STomi Valkeinen 1366f7018c21STomi Valkeinen if (!hwcursor) 1367f7018c21STomi Valkeinen return -EINVAL; /* just to force soft_cursor() call */ 1368f7018c21STomi Valkeinen 1369f7018c21STomi Valkeinen /* Too large of a cursor or wrong bpp :-( */ 1370f7018c21STomi Valkeinen if (cursor->image.width > 64 || 1371f7018c21STomi Valkeinen cursor->image.height > 64 || 1372f7018c21STomi Valkeinen cursor->image.depth > 1) 1373f7018c21STomi Valkeinen return -EINVAL; 1374f7018c21STomi Valkeinen 1375f7018c21STomi Valkeinen if (par->type == PM2_TYPE_PERMEDIA2V) 1376f7018c21STomi Valkeinen return pm2vfb_cursor(info, cursor); 1377f7018c21STomi Valkeinen 1378f7018c21STomi Valkeinen mode = 0x40; 1379f7018c21STomi Valkeinen if (cursor->enable) 1380f7018c21STomi Valkeinen mode = 0x43; 1381f7018c21STomi Valkeinen 1382f7018c21STomi Valkeinen pm2_RDAC_WR(par, PM2I_RD_CURSOR_CONTROL, mode); 1383f7018c21STomi Valkeinen 1384f7018c21STomi Valkeinen /* 1385f7018c21STomi Valkeinen * If the cursor is not be changed this means either we want the 1386f7018c21STomi Valkeinen * current cursor state (if enable is set) or we want to query what 1387f7018c21STomi Valkeinen * we can do with the cursor (if enable is not set) 1388f7018c21STomi Valkeinen */ 1389f7018c21STomi Valkeinen if (!cursor->set) 1390f7018c21STomi Valkeinen return 0; 1391f7018c21STomi Valkeinen 1392f7018c21STomi Valkeinen if (cursor->set & FB_CUR_SETPOS) { 1393f7018c21STomi Valkeinen int x = cursor->image.dx - info->var.xoffset + 63; 1394f7018c21STomi Valkeinen int y = cursor->image.dy - info->var.yoffset + 63; 1395f7018c21STomi Valkeinen 1396f7018c21STomi Valkeinen WAIT_FIFO(par, 4); 1397f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_X_LSB, x & 0xff); 1398f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_X_MSB, (x >> 8) & 0x7); 1399f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_Y_LSB, y & 0xff); 1400f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_Y_MSB, (y >> 8) & 0x7); 1401f7018c21STomi Valkeinen } 1402f7018c21STomi Valkeinen 1403f7018c21STomi Valkeinen if (cursor->set & FB_CUR_SETCMAP) { 1404f7018c21STomi Valkeinen u32 fg_idx = cursor->image.fg_color; 1405f7018c21STomi Valkeinen u32 bg_idx = cursor->image.bg_color; 1406f7018c21STomi Valkeinen 1407f7018c21STomi Valkeinen WAIT_FIFO(par, 7); 1408f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_COLOR_ADDRESS, 1); 1409f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_COLOR_DATA, 1410f7018c21STomi Valkeinen info->cmap.red[bg_idx] >> 8); 1411f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_COLOR_DATA, 1412f7018c21STomi Valkeinen info->cmap.green[bg_idx] >> 8); 1413f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_COLOR_DATA, 1414f7018c21STomi Valkeinen info->cmap.blue[bg_idx] >> 8); 1415f7018c21STomi Valkeinen 1416f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_COLOR_DATA, 1417f7018c21STomi Valkeinen info->cmap.red[fg_idx] >> 8); 1418f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_COLOR_DATA, 1419f7018c21STomi Valkeinen info->cmap.green[fg_idx] >> 8); 1420f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_COLOR_DATA, 1421f7018c21STomi Valkeinen info->cmap.blue[fg_idx] >> 8); 1422f7018c21STomi Valkeinen } 1423f7018c21STomi Valkeinen 1424f7018c21STomi Valkeinen if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) { 1425f7018c21STomi Valkeinen u8 *bitmap = (u8 *)cursor->image.data; 1426f7018c21STomi Valkeinen u8 *mask = (u8 *)cursor->mask; 1427f7018c21STomi Valkeinen int i; 1428f7018c21STomi Valkeinen 1429f7018c21STomi Valkeinen WAIT_FIFO(par, 1); 1430f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_PALETTE_WRITE_ADDRESS, 0); 1431f7018c21STomi Valkeinen 1432f7018c21STomi Valkeinen for (i = 0; i < cursor->image.height; i++) { 1433f7018c21STomi Valkeinen int j = (cursor->image.width + 7) >> 3; 1434f7018c21STomi Valkeinen int k = 8 - j; 1435f7018c21STomi Valkeinen 1436f7018c21STomi Valkeinen WAIT_FIFO(par, 8); 1437f7018c21STomi Valkeinen for (; j > 0; j--) { 1438f7018c21STomi Valkeinen u8 data = *bitmap ^ *mask; 1439f7018c21STomi Valkeinen 1440f7018c21STomi Valkeinen if (cursor->rop == ROP_COPY) 1441f7018c21STomi Valkeinen data = *mask & *bitmap; 1442f7018c21STomi Valkeinen /* bitmap data */ 1443f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_DATA, data); 1444f7018c21STomi Valkeinen bitmap++; 1445f7018c21STomi Valkeinen mask++; 1446f7018c21STomi Valkeinen } 1447f7018c21STomi Valkeinen for (; k > 0; k--) 1448f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_DATA, 0); 1449f7018c21STomi Valkeinen } 1450f7018c21STomi Valkeinen for (; i < 64; i++) { 1451f7018c21STomi Valkeinen int j = 8; 1452f7018c21STomi Valkeinen WAIT_FIFO(par, 8); 1453f7018c21STomi Valkeinen while (j-- > 0) 1454f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_DATA, 0); 1455f7018c21STomi Valkeinen } 1456f7018c21STomi Valkeinen 1457f7018c21STomi Valkeinen mask = (u8 *)cursor->mask; 1458f7018c21STomi Valkeinen for (i = 0; i < cursor->image.height; i++) { 1459f7018c21STomi Valkeinen int j = (cursor->image.width + 7) >> 3; 1460f7018c21STomi Valkeinen int k = 8 - j; 1461f7018c21STomi Valkeinen 1462f7018c21STomi Valkeinen WAIT_FIFO(par, 8); 1463f7018c21STomi Valkeinen for (; j > 0; j--) { 1464f7018c21STomi Valkeinen /* mask */ 1465f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_DATA, *mask); 1466f7018c21STomi Valkeinen mask++; 1467f7018c21STomi Valkeinen } 1468f7018c21STomi Valkeinen for (; k > 0; k--) 1469f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_DATA, 0); 1470f7018c21STomi Valkeinen } 1471f7018c21STomi Valkeinen for (; i < 64; i++) { 1472f7018c21STomi Valkeinen int j = 8; 1473f7018c21STomi Valkeinen WAIT_FIFO(par, 8); 1474f7018c21STomi Valkeinen while (j-- > 0) 1475f7018c21STomi Valkeinen pm2_WR(par, PM2R_RD_CURSOR_DATA, 0); 1476f7018c21STomi Valkeinen } 1477f7018c21STomi Valkeinen } 1478f7018c21STomi Valkeinen return 0; 1479f7018c21STomi Valkeinen } 1480f7018c21STomi Valkeinen 1481f7018c21STomi Valkeinen /* ------------ Hardware Independent Functions ------------ */ 1482f7018c21STomi Valkeinen 1483f7018c21STomi Valkeinen /* 1484f7018c21STomi Valkeinen * Frame buffer operations 1485f7018c21STomi Valkeinen */ 1486f7018c21STomi Valkeinen 14878a48ac33SJani Nikula static const struct fb_ops pm2fb_ops = { 1488f7018c21STomi Valkeinen .owner = THIS_MODULE, 1489f7018c21STomi Valkeinen .fb_check_var = pm2fb_check_var, 1490f7018c21STomi Valkeinen .fb_set_par = pm2fb_set_par, 1491f7018c21STomi Valkeinen .fb_setcolreg = pm2fb_setcolreg, 1492f7018c21STomi Valkeinen .fb_blank = pm2fb_blank, 1493f7018c21STomi Valkeinen .fb_pan_display = pm2fb_pan_display, 1494f7018c21STomi Valkeinen .fb_fillrect = pm2fb_fillrect, 1495f7018c21STomi Valkeinen .fb_copyarea = pm2fb_copyarea, 1496f7018c21STomi Valkeinen .fb_imageblit = pm2fb_imageblit, 1497f7018c21STomi Valkeinen .fb_sync = pm2fb_sync, 1498f7018c21STomi Valkeinen .fb_cursor = pm2fb_cursor, 1499f7018c21STomi Valkeinen }; 1500f7018c21STomi Valkeinen 1501f7018c21STomi Valkeinen /* 1502f7018c21STomi Valkeinen * PCI stuff 1503f7018c21STomi Valkeinen */ 1504f7018c21STomi Valkeinen 1505f7018c21STomi Valkeinen 1506f7018c21STomi Valkeinen /** 1507f7018c21STomi Valkeinen * Device initialisation 1508f7018c21STomi Valkeinen * 1509f7018c21STomi Valkeinen * Initialise and allocate resource for PCI device. 1510f7018c21STomi Valkeinen * 1511*b47e6ca3SSam Ravnborg * @pdev: PCI device. 1512*b47e6ca3SSam Ravnborg * @id: PCI device ID. 1513f7018c21STomi Valkeinen */ 1514f7018c21STomi Valkeinen static int pm2fb_probe(struct pci_dev *pdev, const struct pci_device_id *id) 1515f7018c21STomi Valkeinen { 1516f7018c21STomi Valkeinen struct pm2fb_par *default_par; 1517f7018c21STomi Valkeinen struct fb_info *info; 1518f7018c21STomi Valkeinen int err; 1519f7018c21STomi Valkeinen int retval = -ENXIO; 1520f7018c21STomi Valkeinen 1521f7018c21STomi Valkeinen err = pci_enable_device(pdev); 1522f7018c21STomi Valkeinen if (err) { 1523f7018c21STomi Valkeinen printk(KERN_WARNING "pm2fb: Can't enable pdev: %d\n", err); 1524f7018c21STomi Valkeinen return err; 1525f7018c21STomi Valkeinen } 1526f7018c21STomi Valkeinen 1527f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct pm2fb_par), &pdev->dev); 1528f7018c21STomi Valkeinen if (!info) 1529f7018c21STomi Valkeinen return -ENOMEM; 1530f7018c21STomi Valkeinen default_par = info->par; 1531f7018c21STomi Valkeinen 1532f7018c21STomi Valkeinen switch (pdev->device) { 1533f7018c21STomi Valkeinen case PCI_DEVICE_ID_TI_TVP4020: 1534f7018c21STomi Valkeinen strcpy(pm2fb_fix.id, "TVP4020"); 1535f7018c21STomi Valkeinen default_par->type = PM2_TYPE_PERMEDIA2; 1536f7018c21STomi Valkeinen break; 1537f7018c21STomi Valkeinen case PCI_DEVICE_ID_3DLABS_PERMEDIA2: 1538f7018c21STomi Valkeinen strcpy(pm2fb_fix.id, "Permedia2"); 1539f7018c21STomi Valkeinen default_par->type = PM2_TYPE_PERMEDIA2; 1540f7018c21STomi Valkeinen break; 1541f7018c21STomi Valkeinen case PCI_DEVICE_ID_3DLABS_PERMEDIA2V: 1542f7018c21STomi Valkeinen strcpy(pm2fb_fix.id, "Permedia2v"); 1543f7018c21STomi Valkeinen default_par->type = PM2_TYPE_PERMEDIA2V; 1544f7018c21STomi Valkeinen break; 1545f7018c21STomi Valkeinen } 1546f7018c21STomi Valkeinen 1547f7018c21STomi Valkeinen pm2fb_fix.mmio_start = pci_resource_start(pdev, 0); 1548f7018c21STomi Valkeinen pm2fb_fix.mmio_len = PM2_REGS_SIZE; 1549f7018c21STomi Valkeinen 1550f7018c21STomi Valkeinen #if defined(__BIG_ENDIAN) 1551f7018c21STomi Valkeinen /* 1552f7018c21STomi Valkeinen * PM2 has a 64k register file, mapped twice in 128k. Lower 1553f7018c21STomi Valkeinen * map is little-endian, upper map is big-endian. 1554f7018c21STomi Valkeinen */ 1555f7018c21STomi Valkeinen pm2fb_fix.mmio_start += PM2_REGS_SIZE; 1556f7018c21STomi Valkeinen DPRINTK("Adjusting register base for big-endian.\n"); 1557f7018c21STomi Valkeinen #endif 1558f7018c21STomi Valkeinen DPRINTK("Register base at 0x%lx\n", pm2fb_fix.mmio_start); 1559f7018c21STomi Valkeinen 1560f7018c21STomi Valkeinen /* Registers - request region and map it. */ 1561f7018c21STomi Valkeinen if (!request_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len, 1562f7018c21STomi Valkeinen "pm2fb regbase")) { 1563f7018c21STomi Valkeinen printk(KERN_WARNING "pm2fb: Can't reserve regbase.\n"); 1564f7018c21STomi Valkeinen goto err_exit_neither; 1565f7018c21STomi Valkeinen } 1566f7018c21STomi Valkeinen default_par->v_regs = 15674bdc0d67SChristoph Hellwig ioremap(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len); 1568f7018c21STomi Valkeinen if (!default_par->v_regs) { 1569f7018c21STomi Valkeinen printk(KERN_WARNING "pm2fb: Can't remap %s register area.\n", 1570f7018c21STomi Valkeinen pm2fb_fix.id); 1571f7018c21STomi Valkeinen release_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len); 1572f7018c21STomi Valkeinen goto err_exit_neither; 1573f7018c21STomi Valkeinen } 1574f7018c21STomi Valkeinen 1575f7018c21STomi Valkeinen /* Stash away memory register info for use when we reset the board */ 1576f7018c21STomi Valkeinen default_par->mem_control = pm2_RD(default_par, PM2R_MEM_CONTROL); 1577f7018c21STomi Valkeinen default_par->boot_address = pm2_RD(default_par, PM2R_BOOT_ADDRESS); 1578f7018c21STomi Valkeinen default_par->mem_config = pm2_RD(default_par, PM2R_MEM_CONFIG); 1579f7018c21STomi Valkeinen DPRINTK("MemControl 0x%x BootAddress 0x%x MemConfig 0x%x\n", 1580f7018c21STomi Valkeinen default_par->mem_control, default_par->boot_address, 1581f7018c21STomi Valkeinen default_par->mem_config); 1582f7018c21STomi Valkeinen 1583f7018c21STomi Valkeinen if (default_par->mem_control == 0 && 1584f7018c21STomi Valkeinen default_par->boot_address == 0x31 && 1585f7018c21STomi Valkeinen default_par->mem_config == 0x259fffff) { 1586f7018c21STomi Valkeinen default_par->memclock = CVPPC_MEMCLOCK; 1587f7018c21STomi Valkeinen default_par->mem_control = 0; 1588f7018c21STomi Valkeinen default_par->boot_address = 0x20; 1589f7018c21STomi Valkeinen default_par->mem_config = 0xe6002021; 1590f7018c21STomi Valkeinen if (pdev->subsystem_vendor == 0x1048 && 1591f7018c21STomi Valkeinen pdev->subsystem_device == 0x0a31) { 1592f7018c21STomi Valkeinen DPRINTK("subsystem_vendor: %04x, " 1593f7018c21STomi Valkeinen "subsystem_device: %04x\n", 1594f7018c21STomi Valkeinen pdev->subsystem_vendor, pdev->subsystem_device); 1595f7018c21STomi Valkeinen DPRINTK("We have not been initialized by VGA BIOS and " 1596f7018c21STomi Valkeinen "are running on an Elsa Winner 2000 Office\n"); 1597f7018c21STomi Valkeinen DPRINTK("Initializing card timings manually...\n"); 1598f7018c21STomi Valkeinen default_par->memclock = 100000; 1599f7018c21STomi Valkeinen } 1600f7018c21STomi Valkeinen if (pdev->subsystem_vendor == 0x3d3d && 1601f7018c21STomi Valkeinen pdev->subsystem_device == 0x0100) { 1602f7018c21STomi Valkeinen DPRINTK("subsystem_vendor: %04x, " 1603f7018c21STomi Valkeinen "subsystem_device: %04x\n", 1604f7018c21STomi Valkeinen pdev->subsystem_vendor, pdev->subsystem_device); 1605f7018c21STomi Valkeinen DPRINTK("We have not been initialized by VGA BIOS and " 1606f7018c21STomi Valkeinen "are running on an 3dlabs reference board\n"); 1607f7018c21STomi Valkeinen DPRINTK("Initializing card timings manually...\n"); 1608f7018c21STomi Valkeinen default_par->memclock = 74894; 1609f7018c21STomi Valkeinen } 1610f7018c21STomi Valkeinen } 1611f7018c21STomi Valkeinen 1612f7018c21STomi Valkeinen /* Now work out how big lfb is going to be. */ 1613f7018c21STomi Valkeinen switch (default_par->mem_config & PM2F_MEM_CONFIG_RAM_MASK) { 1614f7018c21STomi Valkeinen case PM2F_MEM_BANKS_1: 1615f7018c21STomi Valkeinen pm2fb_fix.smem_len = 0x200000; 1616f7018c21STomi Valkeinen break; 1617f7018c21STomi Valkeinen case PM2F_MEM_BANKS_2: 1618f7018c21STomi Valkeinen pm2fb_fix.smem_len = 0x400000; 1619f7018c21STomi Valkeinen break; 1620f7018c21STomi Valkeinen case PM2F_MEM_BANKS_3: 1621f7018c21STomi Valkeinen pm2fb_fix.smem_len = 0x600000; 1622f7018c21STomi Valkeinen break; 1623f7018c21STomi Valkeinen case PM2F_MEM_BANKS_4: 1624f7018c21STomi Valkeinen pm2fb_fix.smem_len = 0x800000; 1625f7018c21STomi Valkeinen break; 1626f7018c21STomi Valkeinen } 1627f7018c21STomi Valkeinen pm2fb_fix.smem_start = pci_resource_start(pdev, 1); 1628f7018c21STomi Valkeinen 1629f7018c21STomi Valkeinen /* Linear frame buffer - request region and map it. */ 1630f7018c21STomi Valkeinen if (!request_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len, 1631f7018c21STomi Valkeinen "pm2fb smem")) { 1632f7018c21STomi Valkeinen printk(KERN_WARNING "pm2fb: Can't reserve smem.\n"); 1633f7018c21STomi Valkeinen goto err_exit_mmio; 1634f7018c21STomi Valkeinen } 1635f7018c21STomi Valkeinen info->screen_base = 1636f8f05cdcSLuis R. Rodriguez ioremap_wc(pm2fb_fix.smem_start, pm2fb_fix.smem_len); 1637f7018c21STomi Valkeinen if (!info->screen_base) { 1638f7018c21STomi Valkeinen printk(KERN_WARNING "pm2fb: Can't ioremap smem area.\n"); 1639f7018c21STomi Valkeinen release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len); 1640f7018c21STomi Valkeinen goto err_exit_mmio; 1641f7018c21STomi Valkeinen } 1642f7018c21STomi Valkeinen 1643f7018c21STomi Valkeinen if (!nomtrr) 1644f8f05cdcSLuis R. Rodriguez default_par->wc_cookie = arch_phys_wc_add(pm2fb_fix.smem_start, 1645f8f05cdcSLuis R. Rodriguez pm2fb_fix.smem_len); 1646f7018c21STomi Valkeinen 1647f7018c21STomi Valkeinen info->fbops = &pm2fb_ops; 1648f7018c21STomi Valkeinen info->fix = pm2fb_fix; 1649f7018c21STomi Valkeinen info->pseudo_palette = default_par->palette; 1650f7018c21STomi Valkeinen info->flags = FBINFO_DEFAULT | 1651f7018c21STomi Valkeinen FBINFO_HWACCEL_YPAN | 1652f7018c21STomi Valkeinen FBINFO_HWACCEL_COPYAREA | 1653f7018c21STomi Valkeinen FBINFO_HWACCEL_IMAGEBLIT | 1654f7018c21STomi Valkeinen FBINFO_HWACCEL_FILLRECT; 1655f7018c21STomi Valkeinen 1656f7018c21STomi Valkeinen info->pixmap.addr = kmalloc(PM2_PIXMAP_SIZE, GFP_KERNEL); 1657f7018c21STomi Valkeinen if (!info->pixmap.addr) { 1658f7018c21STomi Valkeinen retval = -ENOMEM; 1659f7018c21STomi Valkeinen goto err_exit_pixmap; 1660f7018c21STomi Valkeinen } 1661f7018c21STomi Valkeinen info->pixmap.size = PM2_PIXMAP_SIZE; 1662f7018c21STomi Valkeinen info->pixmap.buf_align = 4; 1663f7018c21STomi Valkeinen info->pixmap.scan_align = 4; 1664f7018c21STomi Valkeinen info->pixmap.access_align = 32; 1665f7018c21STomi Valkeinen info->pixmap.flags = FB_PIXMAP_SYSTEM; 1666f7018c21STomi Valkeinen 1667f7018c21STomi Valkeinen if (noaccel) { 1668f7018c21STomi Valkeinen printk(KERN_DEBUG "disabling acceleration\n"); 1669f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_DISABLED; 1670f7018c21STomi Valkeinen info->pixmap.scan_align = 1; 1671f7018c21STomi Valkeinen } 1672f7018c21STomi Valkeinen 1673f7018c21STomi Valkeinen if (!mode_option) 1674f7018c21STomi Valkeinen mode_option = "640x480@60"; 1675f7018c21STomi Valkeinen 1676f7018c21STomi Valkeinen err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); 1677f7018c21STomi Valkeinen if (!err || err == 4) 1678f7018c21STomi Valkeinen info->var = pm2fb_var; 1679f7018c21STomi Valkeinen 1680f7018c21STomi Valkeinen retval = fb_alloc_cmap(&info->cmap, 256, 0); 1681f7018c21STomi Valkeinen if (retval < 0) 1682f7018c21STomi Valkeinen goto err_exit_both; 1683f7018c21STomi Valkeinen 1684f7018c21STomi Valkeinen retval = register_framebuffer(info); 1685f7018c21STomi Valkeinen if (retval < 0) 1686f7018c21STomi Valkeinen goto err_exit_all; 1687f7018c21STomi Valkeinen 1688f7018c21STomi Valkeinen fb_info(info, "%s frame buffer device, memory = %dK\n", 1689f7018c21STomi Valkeinen info->fix.id, pm2fb_fix.smem_len / 1024); 1690f7018c21STomi Valkeinen 1691f7018c21STomi Valkeinen /* 1692f7018c21STomi Valkeinen * Our driver data 1693f7018c21STomi Valkeinen */ 1694f7018c21STomi Valkeinen pci_set_drvdata(pdev, info); 1695f7018c21STomi Valkeinen 1696f7018c21STomi Valkeinen return 0; 1697f7018c21STomi Valkeinen 1698f7018c21STomi Valkeinen err_exit_all: 1699f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap); 1700f7018c21STomi Valkeinen err_exit_both: 1701f7018c21STomi Valkeinen kfree(info->pixmap.addr); 1702f7018c21STomi Valkeinen err_exit_pixmap: 1703f7018c21STomi Valkeinen iounmap(info->screen_base); 1704f7018c21STomi Valkeinen release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len); 1705f7018c21STomi Valkeinen err_exit_mmio: 1706f7018c21STomi Valkeinen iounmap(default_par->v_regs); 1707f7018c21STomi Valkeinen release_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len); 1708f7018c21STomi Valkeinen err_exit_neither: 1709f7018c21STomi Valkeinen framebuffer_release(info); 1710f7018c21STomi Valkeinen return retval; 1711f7018c21STomi Valkeinen } 1712f7018c21STomi Valkeinen 1713f7018c21STomi Valkeinen /** 1714f7018c21STomi Valkeinen * Device removal. 1715f7018c21STomi Valkeinen * 1716f7018c21STomi Valkeinen * Release all device resources. 1717f7018c21STomi Valkeinen * 1718*b47e6ca3SSam Ravnborg * @pdev: PCI device to clean up. 1719f7018c21STomi Valkeinen */ 1720f7018c21STomi Valkeinen static void pm2fb_remove(struct pci_dev *pdev) 1721f7018c21STomi Valkeinen { 1722f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(pdev); 1723f7018c21STomi Valkeinen struct fb_fix_screeninfo *fix = &info->fix; 1724f7018c21STomi Valkeinen struct pm2fb_par *par = info->par; 1725f7018c21STomi Valkeinen 1726f7018c21STomi Valkeinen unregister_framebuffer(info); 1727f8f05cdcSLuis R. Rodriguez arch_phys_wc_del(par->wc_cookie); 1728f7018c21STomi Valkeinen iounmap(info->screen_base); 1729f7018c21STomi Valkeinen release_mem_region(fix->smem_start, fix->smem_len); 1730f7018c21STomi Valkeinen iounmap(par->v_regs); 1731f7018c21STomi Valkeinen release_mem_region(fix->mmio_start, fix->mmio_len); 1732f7018c21STomi Valkeinen 1733f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap); 1734f7018c21STomi Valkeinen kfree(info->pixmap.addr); 1735f7018c21STomi Valkeinen framebuffer_release(info); 1736f7018c21STomi Valkeinen } 1737f7018c21STomi Valkeinen 17388bceaa32SArvind Yadav static const struct pci_device_id pm2fb_id_table[] = { 1739f7018c21STomi Valkeinen { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TVP4020, 1740f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 1741f7018c21STomi Valkeinen { PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2, 1742f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 1743f7018c21STomi Valkeinen { PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2V, 1744f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 1745f7018c21STomi Valkeinen { 0, } 1746f7018c21STomi Valkeinen }; 1747f7018c21STomi Valkeinen 1748f7018c21STomi Valkeinen static struct pci_driver pm2fb_driver = { 1749f7018c21STomi Valkeinen .name = "pm2fb", 1750f7018c21STomi Valkeinen .id_table = pm2fb_id_table, 1751f7018c21STomi Valkeinen .probe = pm2fb_probe, 1752f7018c21STomi Valkeinen .remove = pm2fb_remove, 1753f7018c21STomi Valkeinen }; 1754f7018c21STomi Valkeinen 1755f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, pm2fb_id_table); 1756f7018c21STomi Valkeinen 1757f7018c21STomi Valkeinen 1758f7018c21STomi Valkeinen #ifndef MODULE 1759*b47e6ca3SSam Ravnborg /* 1760f7018c21STomi Valkeinen * Parse user specified options. 1761f7018c21STomi Valkeinen * 1762f7018c21STomi Valkeinen * This is, comma-separated options following `video=pm2fb:'. 1763f7018c21STomi Valkeinen */ 1764f7018c21STomi Valkeinen static int __init pm2fb_setup(char *options) 1765f7018c21STomi Valkeinen { 1766f7018c21STomi Valkeinen char *this_opt; 1767f7018c21STomi Valkeinen 1768f7018c21STomi Valkeinen if (!options || !*options) 1769f7018c21STomi Valkeinen return 0; 1770f7018c21STomi Valkeinen 1771f7018c21STomi Valkeinen while ((this_opt = strsep(&options, ",")) != NULL) { 1772f7018c21STomi Valkeinen if (!*this_opt) 1773f7018c21STomi Valkeinen continue; 1774f7018c21STomi Valkeinen if (!strcmp(this_opt, "lowhsync")) 1775f7018c21STomi Valkeinen lowhsync = 1; 1776f7018c21STomi Valkeinen else if (!strcmp(this_opt, "lowvsync")) 1777f7018c21STomi Valkeinen lowvsync = 1; 1778f7018c21STomi Valkeinen else if (!strncmp(this_opt, "hwcursor=", 9)) 1779f7018c21STomi Valkeinen hwcursor = simple_strtoul(this_opt + 9, NULL, 0); 1780f7018c21STomi Valkeinen else if (!strncmp(this_opt, "nomtrr", 6)) 1781f7018c21STomi Valkeinen nomtrr = 1; 1782f7018c21STomi Valkeinen else if (!strncmp(this_opt, "noaccel", 7)) 1783f7018c21STomi Valkeinen noaccel = 1; 1784f7018c21STomi Valkeinen else 1785f7018c21STomi Valkeinen mode_option = this_opt; 1786f7018c21STomi Valkeinen } 1787f7018c21STomi Valkeinen return 0; 1788f7018c21STomi Valkeinen } 1789f7018c21STomi Valkeinen #endif 1790f7018c21STomi Valkeinen 1791f7018c21STomi Valkeinen 1792f7018c21STomi Valkeinen static int __init pm2fb_init(void) 1793f7018c21STomi Valkeinen { 1794f7018c21STomi Valkeinen #ifndef MODULE 1795f7018c21STomi Valkeinen char *option = NULL; 1796f7018c21STomi Valkeinen 1797f7018c21STomi Valkeinen if (fb_get_options("pm2fb", &option)) 1798f7018c21STomi Valkeinen return -ENODEV; 1799f7018c21STomi Valkeinen pm2fb_setup(option); 1800f7018c21STomi Valkeinen #endif 1801f7018c21STomi Valkeinen 1802f7018c21STomi Valkeinen return pci_register_driver(&pm2fb_driver); 1803f7018c21STomi Valkeinen } 1804f7018c21STomi Valkeinen 1805f7018c21STomi Valkeinen module_init(pm2fb_init); 1806f7018c21STomi Valkeinen 1807f7018c21STomi Valkeinen #ifdef MODULE 1808f7018c21STomi Valkeinen /* 1809f7018c21STomi Valkeinen * Cleanup 1810f7018c21STomi Valkeinen */ 1811f7018c21STomi Valkeinen 1812f7018c21STomi Valkeinen static void __exit pm2fb_exit(void) 1813f7018c21STomi Valkeinen { 1814f7018c21STomi Valkeinen pci_unregister_driver(&pm2fb_driver); 1815f7018c21STomi Valkeinen } 1816f7018c21STomi Valkeinen #endif 1817f7018c21STomi Valkeinen 1818f7018c21STomi Valkeinen #ifdef MODULE 1819f7018c21STomi Valkeinen module_exit(pm2fb_exit); 1820f7018c21STomi Valkeinen 1821f7018c21STomi Valkeinen module_param(mode_option, charp, 0); 1822f7018c21STomi Valkeinen MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'"); 1823f7018c21STomi Valkeinen module_param_named(mode, mode_option, charp, 0); 1824f7018c21STomi Valkeinen MODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480-8@60' (deprecated)"); 1825f7018c21STomi Valkeinen module_param(lowhsync, bool, 0); 1826f7018c21STomi Valkeinen MODULE_PARM_DESC(lowhsync, "Force horizontal sync low regardless of mode"); 1827f7018c21STomi Valkeinen module_param(lowvsync, bool, 0); 1828f7018c21STomi Valkeinen MODULE_PARM_DESC(lowvsync, "Force vertical sync low regardless of mode"); 1829f7018c21STomi Valkeinen module_param(noaccel, bool, 0); 1830f7018c21STomi Valkeinen MODULE_PARM_DESC(noaccel, "Disable acceleration"); 1831f7018c21STomi Valkeinen module_param(hwcursor, int, 0644); 1832f7018c21STomi Valkeinen MODULE_PARM_DESC(hwcursor, "Enable hardware cursor " 1833f7018c21STomi Valkeinen "(1=enable, 0=disable, default=1)"); 1834f7018c21STomi Valkeinen module_param(nomtrr, bool, 0); 1835f7018c21STomi Valkeinen MODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)"); 1836f7018c21STomi Valkeinen 1837f7018c21STomi Valkeinen MODULE_AUTHOR("Jim Hague <jim.hague@acm.org>"); 1838f7018c21STomi Valkeinen MODULE_DESCRIPTION("Permedia2 framebuffer device driver"); 1839f7018c21STomi Valkeinen MODULE_LICENSE("GPL"); 1840f7018c21STomi Valkeinen #endif 1841