1841 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Frame buffer driver for Trident TGUI, Blade and Image series4 *5 * Copyright 2001, 2002 - Jani Monoses <jani@iv.ro>6 * Copyright 2009 Krzysztof Helt <krzysztof.h1@wp.pl>7 *8 * CREDITS:(in order of appearance)9 * skeletonfb.c by Geert Uytterhoeven and other fb code in drivers/video10 * Special thanks ;) to Mattia Crivellini <tia@mclink.it>11 * much inspired by the XFree86 4.x Trident driver sources12 * by Alan Hourihane the FreeVGA project13 * Francesco Salvestrini <salvestrini@users.sf.net> XP support,14 * code, suggestions15 * TODO:16 * timing value tweaking so it looks good on every monitor in every mode17 */18 19#include <linux/aperture.h>20#include <linux/module.h>21#include <linux/fb.h>22#include <linux/init.h>23#include <linux/pci.h>24#include <linux/slab.h>25 26#include <linux/delay.h>27#include <video/vga.h>28#include <video/trident.h>29 30#include <linux/i2c.h>31#include <linux/i2c-algo-bit.h>32 33struct tridentfb_par {34 void __iomem *io_virt; /* iospace virtual memory address */35 u32 pseudo_pal[16];36 int chip_id;37 int flatpanel;38 void (*init_accel) (struct tridentfb_par *, int, int);39 void (*wait_engine) (struct tridentfb_par *);40 void (*fill_rect)41 (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);42 void (*copy_rect)43 (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);44 void (*image_blit)45 (struct tridentfb_par *par, const char*,46 u32, u32, u32, u32, u32, u32);47 unsigned char eng_oper; /* engine operation... */48 bool ddc_registered;49 struct i2c_adapter ddc_adapter;50 struct i2c_algo_bit_data ddc_algo;51};52 53static struct fb_fix_screeninfo tridentfb_fix = {54 .id = "Trident",55 .type = FB_TYPE_PACKED_PIXELS,56 .ypanstep = 1,57 .visual = FB_VISUAL_PSEUDOCOLOR,58 .accel = FB_ACCEL_NONE,59};60 61/* defaults which are normally overriden by user values */62 63/* video mode */64static char *mode_option;65static int bpp = 8;66 67static int noaccel;68 69static int center;70static int stretch;71 72static int fp;73static int crt;74 75static int memsize;76static int memdiff;77static int nativex;78 79module_param(mode_option, charp, 0);80MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");81module_param_named(mode, mode_option, charp, 0);82MODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480-8@60' (deprecated)");83module_param(bpp, int, 0);84module_param(center, int, 0);85module_param(stretch, int, 0);86module_param(noaccel, int, 0);87module_param(memsize, int, 0);88module_param(memdiff, int, 0);89module_param(nativex, int, 0);90module_param(fp, int, 0);91MODULE_PARM_DESC(fp, "Define if flatpanel is connected");92module_param(crt, int, 0);93MODULE_PARM_DESC(crt, "Define if CRT is connected");94 95static inline int is_oldclock(int id)96{97 return (id == TGUI9440) ||98 (id == TGUI9660) ||99 (id == CYBER9320);100}101 102static inline int is_oldprotect(int id)103{104 return is_oldclock(id) ||105 (id == PROVIDIA9685) ||106 (id == CYBER9382) ||107 (id == CYBER9385);108}109 110static inline int is_blade(int id)111{112 return (id == BLADE3D) ||113 (id == CYBERBLADEE4) ||114 (id == CYBERBLADEi7) ||115 (id == CYBERBLADEi7D) ||116 (id == CYBERBLADEi1) ||117 (id == CYBERBLADEi1D) ||118 (id == CYBERBLADEAi1) ||119 (id == CYBERBLADEAi1D);120}121 122static inline int is_xp(int id)123{124 return (id == CYBERBLADEXPAi1) ||125 (id == CYBERBLADEXPm8) ||126 (id == CYBERBLADEXPm16);127}128 129static inline int is3Dchip(int id)130{131 return is_blade(id) || is_xp(id) ||132 (id == CYBER9397) || (id == CYBER9397DVD) ||133 (id == CYBER9520) || (id == CYBER9525DVD) ||134 (id == IMAGE975) || (id == IMAGE985);135}136 137static inline int iscyber(int id)138{139 switch (id) {140 case CYBER9388:141 case CYBER9382:142 case CYBER9385:143 case CYBER9397:144 case CYBER9397DVD:145 case CYBER9520:146 case CYBER9525DVD:147 case CYBERBLADEE4:148 case CYBERBLADEi7D:149 case CYBERBLADEi1:150 case CYBERBLADEi1D:151 case CYBERBLADEAi1:152 case CYBERBLADEAi1D:153 case CYBERBLADEXPAi1:154 return 1;155 156 case CYBER9320:157 case CYBERBLADEi7: /* VIA MPV4 integrated version */158 default:159 /* case CYBERBLDAEXPm8: Strange */160 /* case CYBERBLDAEXPm16: Strange */161 return 0;162 }163}164 165static inline void t_outb(struct tridentfb_par *p, u8 val, u16 reg)166{167 fb_writeb(val, p->io_virt + reg);168}169 170static inline u8 t_inb(struct tridentfb_par *p, u16 reg)171{172 return fb_readb(p->io_virt + reg);173}174 175static inline void writemmr(struct tridentfb_par *par, u16 r, u32 v)176{177 fb_writel(v, par->io_virt + r);178}179 180static inline u32 readmmr(struct tridentfb_par *par, u16 r)181{182 return fb_readl(par->io_virt + r);183}184 185#define DDC_SDA_TGUI BIT(0)186#define DDC_SCL_TGUI BIT(1)187#define DDC_SCL_DRIVE_TGUI BIT(2)188#define DDC_SDA_DRIVE_TGUI BIT(3)189#define DDC_MASK_TGUI (DDC_SCL_DRIVE_TGUI | DDC_SDA_DRIVE_TGUI)190 191static void tridentfb_ddc_setscl_tgui(void *data, int val)192{193 struct tridentfb_par *par = data;194 u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;195 196 if (val)197 reg &= ~DDC_SCL_DRIVE_TGUI; /* disable drive - don't drive hi */198 else199 reg |= DDC_SCL_DRIVE_TGUI; /* drive low */200 201 vga_mm_wcrt(par->io_virt, I2C, reg);202}203 204static void tridentfb_ddc_setsda_tgui(void *data, int val)205{206 struct tridentfb_par *par = data;207 u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;208 209 if (val)210 reg &= ~DDC_SDA_DRIVE_TGUI; /* disable drive - don't drive hi */211 else212 reg |= DDC_SDA_DRIVE_TGUI; /* drive low */213 214 vga_mm_wcrt(par->io_virt, I2C, reg);215}216 217static int tridentfb_ddc_getsda_tgui(void *data)218{219 struct tridentfb_par *par = data;220 221 return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_TGUI);222}223 224#define DDC_SDA_IN BIT(0)225#define DDC_SCL_OUT BIT(1)226#define DDC_SDA_OUT BIT(3)227#define DDC_SCL_IN BIT(6)228#define DDC_MASK (DDC_SCL_OUT | DDC_SDA_OUT)229 230static void tridentfb_ddc_setscl(void *data, int val)231{232 struct tridentfb_par *par = data;233 unsigned char reg;234 235 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;236 if (val)237 reg |= DDC_SCL_OUT;238 else239 reg &= ~DDC_SCL_OUT;240 vga_mm_wcrt(par->io_virt, I2C, reg);241}242 243static void tridentfb_ddc_setsda(void *data, int val)244{245 struct tridentfb_par *par = data;246 unsigned char reg;247 248 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;249 if (!val)250 reg |= DDC_SDA_OUT;251 else252 reg &= ~DDC_SDA_OUT;253 vga_mm_wcrt(par->io_virt, I2C, reg);254}255 256static int tridentfb_ddc_getscl(void *data)257{258 struct tridentfb_par *par = data;259 260 return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SCL_IN);261}262 263static int tridentfb_ddc_getsda(void *data)264{265 struct tridentfb_par *par = data;266 267 return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_IN);268}269 270static int tridentfb_setup_ddc_bus(struct fb_info *info)271{272 struct tridentfb_par *par = info->par;273 274 strscpy(par->ddc_adapter.name, info->fix.id,275 sizeof(par->ddc_adapter.name));276 par->ddc_adapter.owner = THIS_MODULE;277 par->ddc_adapter.algo_data = &par->ddc_algo;278 par->ddc_adapter.dev.parent = info->device;279 if (is_oldclock(par->chip_id)) { /* not sure if this check is OK */280 par->ddc_algo.setsda = tridentfb_ddc_setsda_tgui;281 par->ddc_algo.setscl = tridentfb_ddc_setscl_tgui;282 par->ddc_algo.getsda = tridentfb_ddc_getsda_tgui;283 /* no getscl */284 } else {285 par->ddc_algo.setsda = tridentfb_ddc_setsda;286 par->ddc_algo.setscl = tridentfb_ddc_setscl;287 par->ddc_algo.getsda = tridentfb_ddc_getsda;288 par->ddc_algo.getscl = tridentfb_ddc_getscl;289 }290 par->ddc_algo.udelay = 10;291 par->ddc_algo.timeout = 20;292 par->ddc_algo.data = par;293 294 i2c_set_adapdata(&par->ddc_adapter, par);295 296 return i2c_bit_add_bus(&par->ddc_adapter);297}298 299/*300 * Blade specific acceleration.301 */302 303#define point(x, y) ((y) << 16 | (x))304 305static void blade_init_accel(struct tridentfb_par *par, int pitch, int bpp)306{307 int v1 = (pitch >> 3) << 20;308 int tmp = bpp == 24 ? 2 : (bpp >> 4);309 int v2 = v1 | (tmp << 29);310 311 writemmr(par, 0x21C0, v2);312 writemmr(par, 0x21C4, v2);313 writemmr(par, 0x21B8, v2);314 writemmr(par, 0x21BC, v2);315 writemmr(par, 0x21D0, v1);316 writemmr(par, 0x21D4, v1);317 writemmr(par, 0x21C8, v1);318 writemmr(par, 0x21CC, v1);319 writemmr(par, 0x216C, 0);320}321 322static void blade_wait_engine(struct tridentfb_par *par)323{324 while (readmmr(par, STATUS) & 0xFA800000)325 cpu_relax();326}327 328static void blade_fill_rect(struct tridentfb_par *par,329 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)330{331 writemmr(par, COLOR, c);332 writemmr(par, ROP, rop ? ROP_X : ROP_S);333 writemmr(par, CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);334 335 writemmr(par, DST1, point(x, y));336 writemmr(par, DST2, point(x + w - 1, y + h - 1));337}338 339static void blade_image_blit(struct tridentfb_par *par, const char *data,340 u32 x, u32 y, u32 w, u32 h, u32 c, u32 b)341{342 unsigned size = ((w + 31) >> 5) * h;343 344 writemmr(par, COLOR, c);345 writemmr(par, BGCOLOR, b);346 writemmr(par, CMD, 0xa0000000 | 3 << 19);347 348 writemmr(par, DST1, point(x, y));349 writemmr(par, DST2, point(x + w - 1, y + h - 1));350 351 iowrite32_rep(par->io_virt + 0x10000, data, size);352}353 354static void blade_copy_rect(struct tridentfb_par *par,355 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)356{357 int direction = 2;358 u32 s1 = point(x1, y1);359 u32 s2 = point(x1 + w - 1, y1 + h - 1);360 u32 d1 = point(x2, y2);361 u32 d2 = point(x2 + w - 1, y2 + h - 1);362 363 if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))364 direction = 0;365 366 writemmr(par, ROP, ROP_S);367 writemmr(par, CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction);368 369 writemmr(par, SRC1, direction ? s2 : s1);370 writemmr(par, SRC2, direction ? s1 : s2);371 writemmr(par, DST1, direction ? d2 : d1);372 writemmr(par, DST2, direction ? d1 : d2);373}374 375/*376 * BladeXP specific acceleration functions377 */378 379static void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp)380{381 unsigned char x = bpp == 24 ? 3 : (bpp >> 4);382 int v1 = pitch << (bpp == 24 ? 20 : (18 + x));383 384 switch (pitch << (bpp >> 3)) {385 case 8192:386 case 512:387 x |= 0x00;388 break;389 case 1024:390 x |= 0x04;391 break;392 case 2048:393 x |= 0x08;394 break;395 case 4096:396 x |= 0x0C;397 break;398 }399 400 t_outb(par, x, 0x2125);401 402 par->eng_oper = x | 0x40;403 404 writemmr(par, 0x2154, v1);405 writemmr(par, 0x2150, v1);406 t_outb(par, 3, 0x2126);407}408 409static void xp_wait_engine(struct tridentfb_par *par)410{411 int count = 0;412 int timeout = 0;413 414 while (t_inb(par, STATUS) & 0x80) {415 count++;416 if (count == 10000000) {417 /* Timeout */418 count = 9990000;419 timeout++;420 if (timeout == 8) {421 /* Reset engine */422 t_outb(par, 0x00, STATUS);423 return;424 }425 }426 cpu_relax();427 }428}429 430static void xp_fill_rect(struct tridentfb_par *par,431 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)432{433 writemmr(par, 0x2127, ROP_P);434 writemmr(par, 0x2158, c);435 writemmr(par, DRAWFL, 0x4000);436 writemmr(par, OLDDIM, point(h, w));437 writemmr(par, OLDDST, point(y, x));438 t_outb(par, 0x01, OLDCMD);439 t_outb(par, par->eng_oper, 0x2125);440}441 442static void xp_copy_rect(struct tridentfb_par *par,443 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)444{445 u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp;446 int direction = 0x0004;447 448 if ((x1 < x2) && (y1 == y2)) {449 direction |= 0x0200;450 x1_tmp = x1 + w - 1;451 x2_tmp = x2 + w - 1;452 } else {453 x1_tmp = x1;454 x2_tmp = x2;455 }456 457 if (y1 < y2) {458 direction |= 0x0100;459 y1_tmp = y1 + h - 1;460 y2_tmp = y2 + h - 1;461 } else {462 y1_tmp = y1;463 y2_tmp = y2;464 }465 466 writemmr(par, DRAWFL, direction);467 t_outb(par, ROP_S, 0x2127);468 writemmr(par, OLDSRC, point(y1_tmp, x1_tmp));469 writemmr(par, OLDDST, point(y2_tmp, x2_tmp));470 writemmr(par, OLDDIM, point(h, w));471 t_outb(par, 0x01, OLDCMD);472}473 474/*475 * Image specific acceleration functions476 */477static void image_init_accel(struct tridentfb_par *par, int pitch, int bpp)478{479 int tmp = bpp == 24 ? 2: (bpp >> 4);480 481 writemmr(par, 0x2120, 0xF0000000);482 writemmr(par, 0x2120, 0x40000000 | tmp);483 writemmr(par, 0x2120, 0x80000000);484 writemmr(par, 0x2144, 0x00000000);485 writemmr(par, 0x2148, 0x00000000);486 writemmr(par, 0x2150, 0x00000000);487 writemmr(par, 0x2154, 0x00000000);488 writemmr(par, 0x2120, 0x60000000 | (pitch << 16) | pitch);489 writemmr(par, 0x216C, 0x00000000);490 writemmr(par, 0x2170, 0x00000000);491 writemmr(par, 0x217C, 0x00000000);492 writemmr(par, 0x2120, 0x10000000);493 writemmr(par, 0x2130, (2047 << 16) | 2047);494}495 496static void image_wait_engine(struct tridentfb_par *par)497{498 while (readmmr(par, 0x2164) & 0xF0000000)499 cpu_relax();500}501 502static void image_fill_rect(struct tridentfb_par *par,503 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)504{505 writemmr(par, 0x2120, 0x80000000);506 writemmr(par, 0x2120, 0x90000000 | ROP_S);507 508 writemmr(par, 0x2144, c);509 510 writemmr(par, DST1, point(x, y));511 writemmr(par, DST2, point(x + w - 1, y + h - 1));512 513 writemmr(par, 0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9);514}515 516static void image_copy_rect(struct tridentfb_par *par,517 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)518{519 int direction = 0x4;520 u32 s1 = point(x1, y1);521 u32 s2 = point(x1 + w - 1, y1 + h - 1);522 u32 d1 = point(x2, y2);523 u32 d2 = point(x2 + w - 1, y2 + h - 1);524 525 if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))526 direction = 0;527 528 writemmr(par, 0x2120, 0x80000000);529 writemmr(par, 0x2120, 0x90000000 | ROP_S);530 531 writemmr(par, SRC1, direction ? s2 : s1);532 writemmr(par, SRC2, direction ? s1 : s2);533 writemmr(par, DST1, direction ? d2 : d1);534 writemmr(par, DST2, direction ? d1 : d2);535 writemmr(par, 0x2124,536 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction);537}538 539/*540 * TGUI 9440/96XX acceleration541 */542 543static void tgui_init_accel(struct tridentfb_par *par, int pitch, int bpp)544{545 unsigned char x = bpp == 24 ? 3 : (bpp >> 4);546 547 /* disable clipping */548 writemmr(par, 0x2148, 0);549 writemmr(par, 0x214C, point(4095, 2047));550 551 switch ((pitch * bpp) / 8) {552 case 8192:553 case 512:554 x |= 0x00;555 break;556 case 1024:557 x |= 0x04;558 break;559 case 2048:560 x |= 0x08;561 break;562 case 4096:563 x |= 0x0C;564 break;565 }566 567 fb_writew(x, par->io_virt + 0x2122);568}569 570static void tgui_fill_rect(struct tridentfb_par *par,571 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)572{573 t_outb(par, ROP_P, 0x2127);574 writemmr(par, OLDCLR, c);575 writemmr(par, DRAWFL, 0x4020);576 writemmr(par, OLDDIM, point(w - 1, h - 1));577 writemmr(par, OLDDST, point(x, y));578 t_outb(par, 1, OLDCMD);579}580 581static void tgui_copy_rect(struct tridentfb_par *par,582 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)583{584 int flags = 0;585 u16 x1_tmp, x2_tmp, y1_tmp, y2_tmp;586 587 if ((x1 < x2) && (y1 == y2)) {588 flags |= 0x0200;589 x1_tmp = x1 + w - 1;590 x2_tmp = x2 + w - 1;591 } else {592 x1_tmp = x1;593 x2_tmp = x2;594 }595 596 if (y1 < y2) {597 flags |= 0x0100;598 y1_tmp = y1 + h - 1;599 y2_tmp = y2 + h - 1;600 } else {601 y1_tmp = y1;602 y2_tmp = y2;603 }604 605 writemmr(par, DRAWFL, 0x4 | flags);606 t_outb(par, ROP_S, 0x2127);607 writemmr(par, OLDSRC, point(x1_tmp, y1_tmp));608 writemmr(par, OLDDST, point(x2_tmp, y2_tmp));609 writemmr(par, OLDDIM, point(w - 1, h - 1));610 t_outb(par, 1, OLDCMD);611}612 613/*614 * Accel functions called by the upper layers615 */616static void tridentfb_fillrect(struct fb_info *info,617 const struct fb_fillrect *fr)618{619 struct tridentfb_par *par = info->par;620 int col;621 622 if (info->flags & FBINFO_HWACCEL_DISABLED) {623 cfb_fillrect(info, fr);624 return;625 }626 if (info->var.bits_per_pixel == 8) {627 col = fr->color;628 col |= col << 8;629 col |= col << 16;630 } else631 col = ((u32 *)(info->pseudo_palette))[fr->color];632 633 par->wait_engine(par);634 par->fill_rect(par, fr->dx, fr->dy, fr->width,635 fr->height, col, fr->rop);636}637 638static void tridentfb_imageblit(struct fb_info *info,639 const struct fb_image *img)640{641 struct tridentfb_par *par = info->par;642 int col, bgcol;643 644 if ((info->flags & FBINFO_HWACCEL_DISABLED) || img->depth != 1) {645 cfb_imageblit(info, img);646 return;647 }648 if (info->var.bits_per_pixel == 8) {649 col = img->fg_color;650 col |= col << 8;651 col |= col << 16;652 bgcol = img->bg_color;653 bgcol |= bgcol << 8;654 bgcol |= bgcol << 16;655 } else {656 col = ((u32 *)(info->pseudo_palette))[img->fg_color];657 bgcol = ((u32 *)(info->pseudo_palette))[img->bg_color];658 }659 660 par->wait_engine(par);661 if (par->image_blit)662 par->image_blit(par, img->data, img->dx, img->dy,663 img->width, img->height, col, bgcol);664 else665 cfb_imageblit(info, img);666}667 668static void tridentfb_copyarea(struct fb_info *info,669 const struct fb_copyarea *ca)670{671 struct tridentfb_par *par = info->par;672 673 if (info->flags & FBINFO_HWACCEL_DISABLED) {674 cfb_copyarea(info, ca);675 return;676 }677 par->wait_engine(par);678 par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy,679 ca->width, ca->height);680}681 682static int tridentfb_sync(struct fb_info *info)683{684 struct tridentfb_par *par = info->par;685 686 if (!(info->flags & FBINFO_HWACCEL_DISABLED))687 par->wait_engine(par);688 return 0;689}690 691/*692 * Hardware access functions693 */694 695static inline unsigned char read3X4(struct tridentfb_par *par, int reg)696{697 return vga_mm_rcrt(par->io_virt, reg);698}699 700static inline void write3X4(struct tridentfb_par *par, int reg,701 unsigned char val)702{703 vga_mm_wcrt(par->io_virt, reg, val);704}705 706static inline unsigned char read3CE(struct tridentfb_par *par,707 unsigned char reg)708{709 return vga_mm_rgfx(par->io_virt, reg);710}711 712static inline void writeAttr(struct tridentfb_par *par, int reg,713 unsigned char val)714{715 fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */716 vga_mm_wattr(par->io_virt, reg, val);717}718 719static inline void write3CE(struct tridentfb_par *par, int reg,720 unsigned char val)721{722 vga_mm_wgfx(par->io_virt, reg, val);723}724 725static void enable_mmio(struct tridentfb_par *par)726{727 /* Goto New Mode */728 vga_io_rseq(0x0B);729 730 /* Unprotect registers */731 vga_io_wseq(NewMode1, 0x80);732 if (!is_oldprotect(par->chip_id))733 vga_io_wseq(Protection, 0x92);734 735 /* Enable MMIO */736 outb(PCIReg, 0x3D4);737 outb(inb(0x3D5) | 0x01, 0x3D5);738}739 740static void disable_mmio(struct tridentfb_par *par)741{742 /* Goto New Mode */743 vga_mm_rseq(par->io_virt, 0x0B);744 745 /* Unprotect registers */746 vga_mm_wseq(par->io_virt, NewMode1, 0x80);747 if (!is_oldprotect(par->chip_id))748 vga_mm_wseq(par->io_virt, Protection, 0x92);749 750 /* Disable MMIO */751 t_outb(par, PCIReg, 0x3D4);752 t_outb(par, t_inb(par, 0x3D5) & ~0x01, 0x3D5);753}754 755static inline void crtc_unlock(struct tridentfb_par *par)756{757 write3X4(par, VGA_CRTC_V_SYNC_END,758 read3X4(par, VGA_CRTC_V_SYNC_END) & 0x7F);759}760 761/* Return flat panel's maximum x resolution */762static int get_nativex(struct tridentfb_par *par)763{764 int x, y, tmp;765 766 if (nativex)767 return nativex;768 769 tmp = (read3CE(par, VertStretch) >> 4) & 3;770 771 switch (tmp) {772 case 0:773 x = 1280; y = 1024;774 break;775 case 2:776 x = 1024; y = 768;777 break;778 case 3:779 x = 800; y = 600;780 break;781 case 1:782 default:783 x = 640; y = 480;784 break;785 }786 787 output("%dx%d flat panel found\n", x, y);788 return x;789}790 791/* Set pitch */792static inline void set_lwidth(struct tridentfb_par *par, int width)793{794 write3X4(par, VGA_CRTC_OFFSET, width & 0xFF);795 /* chips older than TGUI9660 have only 1 width bit in AddColReg */796 /* touching the other one breaks I2C/DDC */797 if (par->chip_id == TGUI9440 || par->chip_id == CYBER9320)798 write3X4(par, AddColReg,799 (read3X4(par, AddColReg) & 0xEF) | ((width & 0x100) >> 4));800 else801 write3X4(par, AddColReg,802 (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4));803}804 805/* For resolutions smaller than FP resolution stretch */806static void screen_stretch(struct tridentfb_par *par)807{808 if (par->chip_id != CYBERBLADEXPAi1)809 write3CE(par, BiosReg, 0);810 else811 write3CE(par, BiosReg, 8);812 write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 1);813 write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 1);814}815 816/* For resolutions smaller than FP resolution center */817static inline void screen_center(struct tridentfb_par *par)818{819 write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 0x80);820 write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 0x80);821}822 823/* Address of first shown pixel in display memory */824static void set_screen_start(struct tridentfb_par *par, int base)825{826 u8 tmp;827 write3X4(par, VGA_CRTC_START_LO, base & 0xFF);828 write3X4(par, VGA_CRTC_START_HI, (base & 0xFF00) >> 8);829 tmp = read3X4(par, CRTCModuleTest) & 0xDF;830 write3X4(par, CRTCModuleTest, tmp | ((base & 0x10000) >> 11));831 tmp = read3X4(par, CRTHiOrd) & 0xF8;832 write3X4(par, CRTHiOrd, tmp | ((base & 0xE0000) >> 17));833}834 835/* Set dotclock frequency */836static void set_vclk(struct tridentfb_par *par, unsigned long freq)837{838 int m, n, k;839 unsigned long fi, d, di;840 unsigned char best_m = 0, best_n = 0, best_k = 0;841 unsigned char hi, lo;842 unsigned char shift = !is_oldclock(par->chip_id) ? 2 : 1;843 844 d = 20000;845 for (k = shift; k >= 0; k--)846 for (m = 1; m < 32; m++) {847 n = ((m + 2) << shift) - 8;848 for (n = (n < 0 ? 0 : n); n < 122; n++) {849 fi = ((14318l * (n + 8)) / (m + 2)) >> k;850 di = abs(fi - freq);851 if (di < d || (di == d && k == best_k)) {852 d = di;853 best_n = n;854 best_m = m;855 best_k = k;856 }857 if (fi > freq)858 break;859 }860 }861 862 if (is_oldclock(par->chip_id)) {863 lo = best_n | (best_m << 7);864 hi = (best_m >> 1) | (best_k << 4);865 } else {866 lo = best_n;867 hi = best_m | (best_k << 6);868 }869 870 if (is3Dchip(par->chip_id)) {871 vga_mm_wseq(par->io_virt, ClockHigh, hi);872 vga_mm_wseq(par->io_virt, ClockLow, lo);873 } else {874 t_outb(par, lo, 0x43C8);875 t_outb(par, hi, 0x43C9);876 }877 debug("VCLK = %X %X\n", hi, lo);878}879 880/* Set number of lines for flat panels*/881static void set_number_of_lines(struct tridentfb_par *par, int lines)882{883 int tmp = read3CE(par, CyberEnhance) & 0x8F;884 if (lines > 1024)885 tmp |= 0x50;886 else if (lines > 768)887 tmp |= 0x30;888 else if (lines > 600)889 tmp |= 0x20;890 else if (lines > 480)891 tmp |= 0x10;892 write3CE(par, CyberEnhance, tmp);893}894 895/*896 * If we see that FP is active we assume we have one.897 * Otherwise we have a CRT display. User can override.898 */899static int is_flatpanel(struct tridentfb_par *par)900{901 if (fp)902 return 1;903 if (crt || !iscyber(par->chip_id))904 return 0;905 return (read3CE(par, FPConfig) & 0x10) ? 1 : 0;906}907 908/* Try detecting the video memory size */909static unsigned int get_memsize(struct tridentfb_par *par)910{911 unsigned char tmp, tmp2;912 unsigned int k;913 914 /* If memory size provided by user */915 if (memsize)916 k = memsize * Kb;917 else918 switch (par->chip_id) {919 case CYBER9525DVD:920 k = 2560 * Kb;921 break;922 default:923 tmp = read3X4(par, SPR) & 0x0F;924 switch (tmp) {925 926 case 0x01:927 k = 512 * Kb;928 break;929 case 0x02:930 k = 6 * Mb; /* XP */931 break;932 case 0x03:933 k = 1 * Mb;934 break;935 case 0x04:936 k = 8 * Mb;937 break;938 case 0x06:939 k = 10 * Mb; /* XP */940 break;941 case 0x07:942 k = 2 * Mb;943 break;944 case 0x08:945 k = 12 * Mb; /* XP */946 break;947 case 0x0A:948 k = 14 * Mb; /* XP */949 break;950 case 0x0C:951 k = 16 * Mb; /* XP */952 break;953 case 0x0E: /* XP */954 955 tmp2 = vga_mm_rseq(par->io_virt, 0xC1);956 switch (tmp2) {957 case 0x00:958 k = 20 * Mb;959 break;960 case 0x01:961 k = 24 * Mb;962 break;963 case 0x10:964 k = 28 * Mb;965 break;966 case 0x11:967 k = 32 * Mb;968 break;969 default:970 k = 1 * Mb;971 break;972 }973 break;974 975 case 0x0F:976 k = 4 * Mb;977 break;978 default:979 k = 1 * Mb;980 break;981 }982 }983 984 k -= memdiff * Kb;985 output("framebuffer size = %d Kb\n", k / Kb);986 return k;987}988 989/* See if we can handle the video mode described in var */990static int tridentfb_check_var(struct fb_var_screeninfo *var,991 struct fb_info *info)992{993 struct tridentfb_par *par = info->par;994 int bpp = var->bits_per_pixel;995 int line_length;996 int ramdac = 230000; /* 230MHz for most 3D chips */997 debug("enter\n");998 999 if (!var->pixclock)1000 return -EINVAL;1001 1002 /* check color depth */1003 if (bpp == 24)1004 bpp = var->bits_per_pixel = 32;1005 if (bpp != 8 && bpp != 16 && bpp != 32)1006 return -EINVAL;1007 if (par->chip_id == TGUI9440 && bpp == 32)1008 return -EINVAL;1009 /* check whether resolution fits on panel and in memory */1010 if (par->flatpanel && nativex && var->xres > nativex)1011 return -EINVAL;1012 /* various resolution checks */1013 var->xres = (var->xres + 7) & ~0x7;1014 if (var->xres > var->xres_virtual)1015 var->xres_virtual = var->xres;1016 if (var->yres > var->yres_virtual)1017 var->yres_virtual = var->yres;1018 if (var->xres_virtual > 4095 || var->yres > 2048)1019 return -EINVAL;1020 /* prevent from position overflow for acceleration */1021 if (var->yres_virtual > 0xffff)1022 return -EINVAL;1023 line_length = var->xres_virtual * bpp / 8;1024 1025 if (!is3Dchip(par->chip_id) &&1026 !(info->flags & FBINFO_HWACCEL_DISABLED)) {1027 /* acceleration requires line length to be power of 2 */1028 if (line_length <= 512)1029 var->xres_virtual = 512 * 8 / bpp;1030 else if (line_length <= 1024)1031 var->xres_virtual = 1024 * 8 / bpp;1032 else if (line_length <= 2048)1033 var->xres_virtual = 2048 * 8 / bpp;1034 else if (line_length <= 4096)1035 var->xres_virtual = 4096 * 8 / bpp;1036 else if (line_length <= 8192)1037 var->xres_virtual = 8192 * 8 / bpp;1038 else1039 return -EINVAL;1040 1041 line_length = var->xres_virtual * bpp / 8;1042 }1043 1044 /* datasheet specifies how to set panning only up to 4 MB */1045 if (line_length * (var->yres_virtual - var->yres) > (4 << 20))1046 var->yres_virtual = ((4 << 20) / line_length) + var->yres;1047 1048 if (line_length * var->yres_virtual > info->fix.smem_len)1049 return -EINVAL;1050 1051 switch (bpp) {1052 case 8:1053 var->red.offset = 0;1054 var->red.length = 8;1055 var->green = var->red;1056 var->blue = var->red;1057 break;1058 case 16:1059 var->red.offset = 11;1060 var->green.offset = 5;1061 var->blue.offset = 0;1062 var->red.length = 5;1063 var->green.length = 6;1064 var->blue.length = 5;1065 break;1066 case 32:1067 var->red.offset = 16;1068 var->green.offset = 8;1069 var->blue.offset = 0;1070 var->red.length = 8;1071 var->green.length = 8;1072 var->blue.length = 8;1073 break;1074 default:1075 return -EINVAL;1076 }1077 1078 if (is_xp(par->chip_id))1079 ramdac = 350000;1080 1081 switch (par->chip_id) {1082 case TGUI9440:1083 ramdac = (bpp >= 16) ? 45000 : 90000;1084 break;1085 case CYBER9320:1086 case TGUI9660:1087 ramdac = 135000;1088 break;1089 case PROVIDIA9685:1090 case CYBER9388:1091 case CYBER9382:1092 case CYBER9385:1093 ramdac = 170000;1094 break;1095 }1096 1097 /* The clock is doubled for 32 bpp */1098 if (bpp == 32)1099 ramdac /= 2;1100 1101 if (PICOS2KHZ(var->pixclock) > ramdac)1102 return -EINVAL;1103 1104 debug("exit\n");1105 1106 return 0;1107 1108}1109 1110/* Pan the display */1111static int tridentfb_pan_display(struct fb_var_screeninfo *var,1112 struct fb_info *info)1113{1114 struct tridentfb_par *par = info->par;1115 unsigned int offset;1116 1117 debug("enter\n");1118 offset = (var->xoffset + (var->yoffset * info->var.xres_virtual))1119 * info->var.bits_per_pixel / 32;1120 set_screen_start(par, offset);1121 debug("exit\n");1122 return 0;1123}1124 1125static inline void shadowmode_on(struct tridentfb_par *par)1126{1127 write3CE(par, CyberControl, read3CE(par, CyberControl) | 0x81);1128}1129 1130/* Set the hardware to the requested video mode */1131static int tridentfb_set_par(struct fb_info *info)1132{1133 struct tridentfb_par *par = info->par;1134 u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend;1135 u32 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend;1136 struct fb_var_screeninfo *var = &info->var;1137 int bpp = var->bits_per_pixel;1138 unsigned char tmp;1139 unsigned long vclk;1140 1141 debug("enter\n");1142 hdispend = var->xres / 8 - 1;1143 hsyncstart = (var->xres + var->right_margin) / 8;1144 hsyncend = (var->xres + var->right_margin + var->hsync_len) / 8;1145 htotal = (var->xres + var->left_margin + var->right_margin +1146 var->hsync_len) / 8 - 5;1147 hblankstart = hdispend + 1;1148 hblankend = htotal + 3;1149 1150 vdispend = var->yres - 1;1151 vsyncstart = var->yres + var->lower_margin;1152 vsyncend = vsyncstart + var->vsync_len;1153 vtotal = var->upper_margin + vsyncend - 2;1154 vblankstart = vdispend + 1;1155 vblankend = vtotal;1156 1157 if (info->var.vmode & FB_VMODE_INTERLACED) {1158 vtotal /= 2;1159 vdispend /= 2;1160 vsyncstart /= 2;1161 vsyncend /= 2;1162 vblankstart /= 2;1163 vblankend /= 2;1164 }1165 1166 enable_mmio(par);1167 crtc_unlock(par);1168 write3CE(par, CyberControl, 8);1169 tmp = 0xEB;1170 if (var->sync & FB_SYNC_HOR_HIGH_ACT)1171 tmp &= ~0x40;1172 if (var->sync & FB_SYNC_VERT_HIGH_ACT)1173 tmp &= ~0x80;1174 1175 if (par->flatpanel && var->xres < nativex) {1176 /*1177 * on flat panels with native size larger1178 * than requested resolution decide whether1179 * we stretch or center1180 */1181 t_outb(par, tmp | 0xC0, VGA_MIS_W);1182 1183 shadowmode_on(par);1184 1185 if (center)1186 screen_center(par);1187 else if (stretch)1188 screen_stretch(par);1189 1190 } else {1191 t_outb(par, tmp, VGA_MIS_W);1192 write3CE(par, CyberControl, 8);1193 }1194 1195 /* vertical timing values */1196 write3X4(par, VGA_CRTC_V_TOTAL, vtotal & 0xFF);1197 write3X4(par, VGA_CRTC_V_DISP_END, vdispend & 0xFF);1198 write3X4(par, VGA_CRTC_V_SYNC_START, vsyncstart & 0xFF);1199 write3X4(par, VGA_CRTC_V_SYNC_END, (vsyncend & 0x0F));1200 write3X4(par, VGA_CRTC_V_BLANK_START, vblankstart & 0xFF);1201 write3X4(par, VGA_CRTC_V_BLANK_END, vblankend & 0xFF);1202 1203 /* horizontal timing values */1204 write3X4(par, VGA_CRTC_H_TOTAL, htotal & 0xFF);1205 write3X4(par, VGA_CRTC_H_DISP, hdispend & 0xFF);1206 write3X4(par, VGA_CRTC_H_SYNC_START, hsyncstart & 0xFF);1207 write3X4(par, VGA_CRTC_H_SYNC_END,1208 (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));1209 write3X4(par, VGA_CRTC_H_BLANK_START, hblankstart & 0xFF);1210 write3X4(par, VGA_CRTC_H_BLANK_END, hblankend & 0x1F);1211 1212 /* higher bits of vertical timing values */1213 tmp = 0x10;1214 if (vtotal & 0x100) tmp |= 0x01;1215 if (vdispend & 0x100) tmp |= 0x02;1216 if (vsyncstart & 0x100) tmp |= 0x04;1217 if (vblankstart & 0x100) tmp |= 0x08;1218 1219 if (vtotal & 0x200) tmp |= 0x20;1220 if (vdispend & 0x200) tmp |= 0x40;1221 if (vsyncstart & 0x200) tmp |= 0x80;1222 write3X4(par, VGA_CRTC_OVERFLOW, tmp);1223 1224 tmp = read3X4(par, CRTHiOrd) & 0x07;1225 tmp |= 0x08; /* line compare bit 10 */1226 if (vtotal & 0x400) tmp |= 0x80;1227 if (vblankstart & 0x400) tmp |= 0x40;1228 if (vsyncstart & 0x400) tmp |= 0x20;1229 if (vdispend & 0x400) tmp |= 0x10;1230 write3X4(par, CRTHiOrd, tmp);1231 1232 tmp = (htotal >> 8) & 0x01;1233 tmp |= (hdispend >> 7) & 0x02;1234 tmp |= (hsyncstart >> 5) & 0x08;1235 tmp |= (hblankstart >> 4) & 0x10;1236 write3X4(par, HorizOverflow, tmp);1237 1238 tmp = 0x40;1239 if (vblankstart & 0x200) tmp |= 0x20;1240//FIXME if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80; /* double scan for 200 line modes */1241 write3X4(par, VGA_CRTC_MAX_SCAN, tmp);1242 1243 write3X4(par, VGA_CRTC_LINE_COMPARE, 0xFF);1244 write3X4(par, VGA_CRTC_PRESET_ROW, 0);1245 write3X4(par, VGA_CRTC_MODE, 0xC3);1246 1247 write3X4(par, LinearAddReg, 0x20); /* enable linear addressing */1248 1249 tmp = (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80;1250 /* enable access extended memory */1251 write3X4(par, CRTCModuleTest, tmp);1252 tmp = read3CE(par, MiscIntContReg) & ~0x4;1253 if (info->var.vmode & FB_VMODE_INTERLACED)1254 tmp |= 0x4;1255 write3CE(par, MiscIntContReg, tmp);1256 1257 /* enable GE for text acceleration */1258 write3X4(par, GraphEngReg, 0x80);1259 1260 switch (bpp) {1261 case 8:1262 tmp = 0x00;1263 break;1264 case 16:1265 tmp = 0x05;1266 break;1267 case 24:1268 tmp = 0x29;1269 break;1270 case 32:1271 tmp = 0x09;1272 break;1273 }1274 1275 write3X4(par, PixelBusReg, tmp);1276 1277 tmp = read3X4(par, DRAMControl);1278 if (!is_oldprotect(par->chip_id))1279 tmp |= 0x10;1280 if (iscyber(par->chip_id))1281 tmp |= 0x20;1282 write3X4(par, DRAMControl, tmp); /* both IO, linear enable */1283 1284 write3X4(par, InterfaceSel, read3X4(par, InterfaceSel) | 0x40);1285 if (!is_xp(par->chip_id))1286 write3X4(par, Performance, read3X4(par, Performance) | 0x10);1287 /* MMIO & PCI read and write burst enable */1288 if (par->chip_id != TGUI9440 && par->chip_id != IMAGE975)1289 write3X4(par, PCIReg, read3X4(par, PCIReg) | 0x06);1290 1291 vga_mm_wseq(par->io_virt, 0, 3);1292 vga_mm_wseq(par->io_virt, 1, 1); /* set char clock 8 dots wide */1293 /* enable 4 maps because needed in chain4 mode */1294 vga_mm_wseq(par->io_virt, 2, 0x0F);1295 vga_mm_wseq(par->io_virt, 3, 0);1296 vga_mm_wseq(par->io_virt, 4, 0x0E); /* memory mode enable bitmaps ?? */1297 1298 /* convert from picoseconds to kHz */1299 vclk = PICOS2KHZ(info->var.pixclock);1300 1301 /* divide clock by 2 if 32bpp chain4 mode display and CPU path */1302 tmp = read3CE(par, MiscExtFunc) & 0xF0;1303 if (bpp == 32 || (par->chip_id == TGUI9440 && bpp == 16)) {1304 tmp |= 8;1305 vclk *= 2;1306 }1307 set_vclk(par, vclk);1308 write3CE(par, MiscExtFunc, tmp | 0x12);1309 write3CE(par, 0x5, 0x40); /* no CGA compat, allow 256 col */1310 write3CE(par, 0x6, 0x05); /* graphics mode */1311 write3CE(par, 0x7, 0x0F); /* planes? */1312 1313 /* graphics mode and support 256 color modes */1314 writeAttr(par, 0x10, 0x41);1315 writeAttr(par, 0x12, 0x0F); /* planes */1316 writeAttr(par, 0x13, 0); /* horizontal pel panning */1317 1318 /* colors */1319 for (tmp = 0; tmp < 0x10; tmp++)1320 writeAttr(par, tmp, tmp);1321 fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */1322 t_outb(par, 0x20, VGA_ATT_W); /* enable attr */1323 1324 switch (bpp) {1325 case 8:1326 tmp = 0;1327 break;1328 case 16:1329 tmp = 0x30;1330 break;1331 case 24:1332 case 32:1333 tmp = 0xD0;1334 break;1335 }1336 1337 t_inb(par, VGA_PEL_IW);1338 t_inb(par, VGA_PEL_MSK);1339 t_inb(par, VGA_PEL_MSK);1340 t_inb(par, VGA_PEL_MSK);1341 t_inb(par, VGA_PEL_MSK);1342 t_outb(par, tmp, VGA_PEL_MSK);1343 t_inb(par, VGA_PEL_IW);1344 1345 if (par->flatpanel)1346 set_number_of_lines(par, info->var.yres);1347 info->fix.line_length = info->var.xres_virtual * bpp / 8;1348 set_lwidth(par, info->fix.line_length / 8);1349 1350 if (!(info->flags & FBINFO_HWACCEL_DISABLED))1351 par->init_accel(par, info->var.xres_virtual, bpp);1352 1353 info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;1354 info->cmap.len = (bpp == 8) ? 256 : 16;1355 debug("exit\n");1356 return 0;1357}1358 1359/* Set one color register */1360static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green,1361 unsigned blue, unsigned transp,1362 struct fb_info *info)1363{1364 int bpp = info->var.bits_per_pixel;1365 struct tridentfb_par *par = info->par;1366 1367 if (regno >= info->cmap.len)1368 return 1;1369 1370 if (bpp == 8) {1371 t_outb(par, 0xFF, VGA_PEL_MSK);1372 t_outb(par, regno, VGA_PEL_IW);1373 1374 t_outb(par, red >> 10, VGA_PEL_D);1375 t_outb(par, green >> 10, VGA_PEL_D);1376 t_outb(par, blue >> 10, VGA_PEL_D);1377 1378 } else if (regno < 16) {1379 if (bpp == 16) { /* RGB 565 */1380 u32 col;1381 1382 col = (red & 0xF800) | ((green & 0xFC00) >> 5) |1383 ((blue & 0xF800) >> 11);1384 col |= col << 16;1385 ((u32 *)(info->pseudo_palette))[regno] = col;1386 } else if (bpp == 32) /* ARGB 8888 */1387 ((u32 *)info->pseudo_palette)[regno] =1388 ((transp & 0xFF00) << 16) |1389 ((red & 0xFF00) << 8) |1390 ((green & 0xFF00)) |1391 ((blue & 0xFF00) >> 8);1392 }1393 1394 return 0;1395}1396 1397/* Try blanking the screen. For flat panels it does nothing */1398static int tridentfb_blank(int blank_mode, struct fb_info *info)1399{1400 unsigned char PMCont, DPMSCont;1401 struct tridentfb_par *par = info->par;1402 1403 debug("enter\n");1404 if (par->flatpanel)1405 return 0;1406 t_outb(par, 0x04, 0x83C8); /* Read DPMS Control */1407 PMCont = t_inb(par, 0x83C6) & 0xFC;1408 DPMSCont = read3CE(par, PowerStatus) & 0xFC;1409 switch (blank_mode) {1410 case FB_BLANK_UNBLANK:1411 /* Screen: On, HSync: On, VSync: On */1412 case FB_BLANK_NORMAL:1413 /* Screen: Off, HSync: On, VSync: On */1414 PMCont |= 0x03;1415 DPMSCont |= 0x00;1416 break;1417 case FB_BLANK_HSYNC_SUSPEND:1418 /* Screen: Off, HSync: Off, VSync: On */1419 PMCont |= 0x02;1420 DPMSCont |= 0x01;1421 break;1422 case FB_BLANK_VSYNC_SUSPEND:1423 /* Screen: Off, HSync: On, VSync: Off */1424 PMCont |= 0x02;1425 DPMSCont |= 0x02;1426 break;1427 case FB_BLANK_POWERDOWN:1428 /* Screen: Off, HSync: Off, VSync: Off */1429 PMCont |= 0x00;1430 DPMSCont |= 0x03;1431 break;1432 }1433 1434 write3CE(par, PowerStatus, DPMSCont);1435 t_outb(par, 4, 0x83C8);1436 t_outb(par, PMCont, 0x83C6);1437 1438 debug("exit\n");1439 1440 /* let fbcon do a softblank for us */1441 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;1442}1443 1444static const struct fb_ops tridentfb_ops = {1445 .owner = THIS_MODULE,1446 __FB_DEFAULT_IOMEM_OPS_RDWR,1447 .fb_setcolreg = tridentfb_setcolreg,1448 .fb_pan_display = tridentfb_pan_display,1449 .fb_blank = tridentfb_blank,1450 .fb_check_var = tridentfb_check_var,1451 .fb_set_par = tridentfb_set_par,1452 .fb_fillrect = tridentfb_fillrect,1453 .fb_copyarea = tridentfb_copyarea,1454 .fb_imageblit = tridentfb_imageblit,1455 .fb_sync = tridentfb_sync,1456 __FB_DEFAULT_IOMEM_OPS_MMAP,1457};1458 1459static int trident_pci_probe(struct pci_dev *dev,1460 const struct pci_device_id *id)1461{1462 int err;1463 unsigned char revision;1464 struct fb_info *info;1465 struct tridentfb_par *default_par;1466 int chip3D;1467 int chip_id;1468 bool found = false;1469 1470 err = aperture_remove_conflicting_pci_devices(dev, "tridentfb");1471 if (err)1472 return err;1473 1474 err = pcim_enable_device(dev);1475 if (err)1476 return err;1477 1478 info = framebuffer_alloc(sizeof(struct tridentfb_par), &dev->dev);1479 if (!info)1480 return -ENOMEM;1481 default_par = info->par;1482 1483 chip_id = id->device;1484 1485 /* If PCI id is 0x9660 then further detect chip type */1486 1487 if (chip_id == TGUI9660) {1488 revision = vga_io_rseq(RevisionID);1489 1490 switch (revision) {1491 case 0x21:1492 chip_id = PROVIDIA9685;1493 break;1494 case 0x22:1495 case 0x23:1496 chip_id = CYBER9397;1497 break;1498 case 0x2A:1499 chip_id = CYBER9397DVD;1500 break;1501 case 0x30:1502 case 0x33:1503 case 0x34:1504 case 0x35:1505 case 0x38:1506 case 0x3A:1507 case 0xB3:1508 chip_id = CYBER9385;1509 break;1510 case 0x40 ... 0x43:1511 chip_id = CYBER9382;1512 break;1513 case 0x4A:1514 chip_id = CYBER9388;1515 break;1516 default:1517 break;1518 }1519 }1520 1521 chip3D = is3Dchip(chip_id);1522 1523 if (is_xp(chip_id)) {1524 default_par->init_accel = xp_init_accel;1525 default_par->wait_engine = xp_wait_engine;1526 default_par->fill_rect = xp_fill_rect;1527 default_par->copy_rect = xp_copy_rect;1528 tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADEXP;1529 } else if (is_blade(chip_id)) {1530 default_par->init_accel = blade_init_accel;1531 default_par->wait_engine = blade_wait_engine;1532 default_par->fill_rect = blade_fill_rect;1533 default_par->copy_rect = blade_copy_rect;1534 default_par->image_blit = blade_image_blit;1535 tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D;1536 } else if (chip3D) { /* 3DImage family left */1537 default_par->init_accel = image_init_accel;1538 default_par->wait_engine = image_wait_engine;1539 default_par->fill_rect = image_fill_rect;1540 default_par->copy_rect = image_copy_rect;1541 tridentfb_fix.accel = FB_ACCEL_TRIDENT_3DIMAGE;1542 } else { /* TGUI 9440/96XX family */1543 default_par->init_accel = tgui_init_accel;1544 default_par->wait_engine = xp_wait_engine;1545 default_par->fill_rect = tgui_fill_rect;1546 default_par->copy_rect = tgui_copy_rect;1547 tridentfb_fix.accel = FB_ACCEL_TRIDENT_TGUI;1548 }1549 1550 default_par->chip_id = chip_id;1551 1552 /* setup MMIO region */1553 tridentfb_fix.mmio_start = pci_resource_start(dev, 1);1554 tridentfb_fix.mmio_len = pci_resource_len(dev, 1);1555 1556 if (!request_mem_region(tridentfb_fix.mmio_start,1557 tridentfb_fix.mmio_len, "tridentfb")) {1558 debug("request_region failed!\n");1559 framebuffer_release(info);1560 return -1;1561 }1562 1563 default_par->io_virt = ioremap(tridentfb_fix.mmio_start,1564 tridentfb_fix.mmio_len);1565 1566 if (!default_par->io_virt) {1567 debug("ioremap failed\n");1568 err = -1;1569 goto out_unmap1;1570 }1571 1572 enable_mmio(default_par);1573 1574 /* setup framebuffer memory */1575 tridentfb_fix.smem_start = pci_resource_start(dev, 0);1576 tridentfb_fix.smem_len = get_memsize(default_par);1577 1578 if (!request_mem_region(tridentfb_fix.smem_start,1579 tridentfb_fix.smem_len, "tridentfb")) {1580 debug("request_mem_region failed!\n");1581 disable_mmio(info->par);1582 err = -1;1583 goto out_unmap1;1584 }1585 1586 info->screen_base = ioremap(tridentfb_fix.smem_start,1587 tridentfb_fix.smem_len);1588 1589 if (!info->screen_base) {1590 debug("ioremap failed\n");1591 err = -1;1592 goto out_unmap2;1593 }1594 1595 default_par->flatpanel = is_flatpanel(default_par);1596 1597 if (default_par->flatpanel)1598 nativex = get_nativex(default_par);1599 1600 info->fix = tridentfb_fix;1601 info->fbops = &tridentfb_ops;1602 info->pseudo_palette = default_par->pseudo_pal;1603 1604 info->flags = FBINFO_HWACCEL_YPAN;1605 if (!noaccel && default_par->init_accel) {1606 info->flags &= ~FBINFO_HWACCEL_DISABLED;1607 info->flags |= FBINFO_HWACCEL_COPYAREA;1608 info->flags |= FBINFO_HWACCEL_FILLRECT;1609 } else1610 info->flags |= FBINFO_HWACCEL_DISABLED;1611 1612 if (is_blade(chip_id) && chip_id != BLADE3D)1613 info->flags |= FBINFO_READS_FAST;1614 1615 info->pixmap.addr = kmalloc(4096, GFP_KERNEL);1616 if (!info->pixmap.addr) {1617 err = -ENOMEM;1618 goto out_unmap2;1619 }1620 1621 info->pixmap.size = 4096;1622 info->pixmap.buf_align = 4;1623 info->pixmap.scan_align = 1;1624 info->pixmap.access_align = 32;1625 info->pixmap.flags = FB_PIXMAP_SYSTEM;1626 info->var.bits_per_pixel = 8;1627 1628 if (default_par->image_blit) {1629 info->flags |= FBINFO_HWACCEL_IMAGEBLIT;1630 info->pixmap.scan_align = 4;1631 }1632 1633 if (noaccel) {1634 printk(KERN_DEBUG "disabling acceleration\n");1635 info->flags |= FBINFO_HWACCEL_DISABLED;1636 info->pixmap.scan_align = 1;1637 }1638 1639 if (tridentfb_setup_ddc_bus(info) == 0) {1640 u8 *edid = fb_ddc_read(&default_par->ddc_adapter);1641 1642 default_par->ddc_registered = true;1643 if (edid) {1644 fb_edid_to_monspecs(edid, &info->monspecs);1645 kfree(edid);1646 if (!info->monspecs.modedb)1647 dev_err(info->device, "error getting mode database\n");1648 else {1649 const struct fb_videomode *m;1650 1651 fb_videomode_to_modelist(info->monspecs.modedb,1652 info->monspecs.modedb_len,1653 &info->modelist);1654 m = fb_find_best_display(&info->monspecs,1655 &info->modelist);1656 if (m) {1657 fb_videomode_to_var(&info->var, m);1658 /* fill all other info->var's fields */1659 if (tridentfb_check_var(&info->var,1660 info) == 0)1661 found = true;1662 }1663 }1664 }1665 }1666 1667 if (!mode_option && !found)1668 mode_option = "640x480-8@60";1669 1670 /* Prepare startup mode */1671 if (mode_option) {1672 err = fb_find_mode(&info->var, info, mode_option,1673 info->monspecs.modedb,1674 info->monspecs.modedb_len,1675 NULL, info->var.bits_per_pixel);1676 if (!err || err == 4) {1677 err = -EINVAL;1678 dev_err(info->device, "mode %s not found\n",1679 mode_option);1680 fb_destroy_modedb(info->monspecs.modedb);1681 info->monspecs.modedb = NULL;1682 goto out_unmap2;1683 }1684 }1685 1686 fb_destroy_modedb(info->monspecs.modedb);1687 info->monspecs.modedb = NULL;1688 1689 err = fb_alloc_cmap(&info->cmap, 256, 0);1690 if (err < 0)1691 goto out_unmap2;1692 1693 info->var.activate |= FB_ACTIVATE_NOW;1694 info->device = &dev->dev;1695 if (register_framebuffer(info) < 0) {1696 printk(KERN_ERR "tridentfb: could not register framebuffer\n");1697 fb_dealloc_cmap(&info->cmap);1698 err = -EINVAL;1699 goto out_unmap2;1700 }1701 output("fb%d: %s frame buffer device %dx%d-%dbpp\n",1702 info->node, info->fix.id, info->var.xres,1703 info->var.yres, info->var.bits_per_pixel);1704 1705 pci_set_drvdata(dev, info);1706 return 0;1707 1708out_unmap2:1709 if (default_par->ddc_registered)1710 i2c_del_adapter(&default_par->ddc_adapter);1711 kfree(info->pixmap.addr);1712 if (info->screen_base)1713 iounmap(info->screen_base);1714 disable_mmio(info->par);1715out_unmap1:1716 if (default_par->io_virt)1717 iounmap(default_par->io_virt);1718 framebuffer_release(info);1719 return err;1720}1721 1722static void trident_pci_remove(struct pci_dev *dev)1723{1724 struct fb_info *info = pci_get_drvdata(dev);1725 struct tridentfb_par *par = info->par;1726 1727 unregister_framebuffer(info);1728 if (par->ddc_registered)1729 i2c_del_adapter(&par->ddc_adapter);1730 iounmap(par->io_virt);1731 iounmap(info->screen_base);1732 kfree(info->pixmap.addr);1733 fb_dealloc_cmap(&info->cmap);1734 framebuffer_release(info);1735}1736 1737/* List of boards that we are trying to support */1738static const struct pci_device_id trident_devices[] = {1739 {PCI_VENDOR_ID_TRIDENT, BLADE3D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1740 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1741 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1742 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1743 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1744 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1745 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1746 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1747 {PCI_VENDOR_ID_TRIDENT, TGUI9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1748 {PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1749 {PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1750 {PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1751 {PCI_VENDOR_ID_TRIDENT, CYBER9320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1752 {PCI_VENDOR_ID_TRIDENT, CYBER9388, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1753 {PCI_VENDOR_ID_TRIDENT, CYBER9520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1754 {PCI_VENDOR_ID_TRIDENT, CYBER9525DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1755 {PCI_VENDOR_ID_TRIDENT, CYBER9397, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1756 {PCI_VENDOR_ID_TRIDENT, CYBER9397DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1757 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1758 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1759 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm16, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},1760 {0,}1761};1762 1763MODULE_DEVICE_TABLE(pci, trident_devices);1764 1765static struct pci_driver tridentfb_pci_driver = {1766 .name = "tridentfb",1767 .id_table = trident_devices,1768 .probe = trident_pci_probe,1769 .remove = trident_pci_remove,1770};1771 1772/*1773 * Parse user specified options (`video=trident:')1774 * example:1775 * video=trident:800x600,bpp=16,noaccel1776 */1777#ifndef MODULE1778static int __init tridentfb_setup(char *options)1779{1780 char *opt;1781 if (!options || !*options)1782 return 0;1783 while ((opt = strsep(&options, ",")) != NULL) {1784 if (!*opt)1785 continue;1786 if (!strncmp(opt, "noaccel", 7))1787 noaccel = 1;1788 else if (!strncmp(opt, "fp", 2))1789 fp = 1;1790 else if (!strncmp(opt, "crt", 3))1791 fp = 0;1792 else if (!strncmp(opt, "bpp=", 4))1793 bpp = simple_strtoul(opt + 4, NULL, 0);1794 else if (!strncmp(opt, "center", 6))1795 center = 1;1796 else if (!strncmp(opt, "stretch", 7))1797 stretch = 1;1798 else if (!strncmp(opt, "memsize=", 8))1799 memsize = simple_strtoul(opt + 8, NULL, 0);1800 else if (!strncmp(opt, "memdiff=", 8))1801 memdiff = simple_strtoul(opt + 8, NULL, 0);1802 else if (!strncmp(opt, "nativex=", 8))1803 nativex = simple_strtoul(opt + 8, NULL, 0);1804 else1805 mode_option = opt;1806 }1807 return 0;1808}1809#endif1810 1811static int __init tridentfb_init(void)1812{1813#ifndef MODULE1814 char *option = NULL;1815#endif1816 1817 if (fb_modesetting_disabled("tridentfb"))1818 return -ENODEV;1819 1820#ifndef MODULE1821 if (fb_get_options("tridentfb", &option))1822 return -ENODEV;1823 tridentfb_setup(option);1824#endif1825 return pci_register_driver(&tridentfb_pci_driver);1826}1827 1828static void __exit tridentfb_exit(void)1829{1830 pci_unregister_driver(&tridentfb_pci_driver);1831}1832 1833module_init(tridentfb_init);1834module_exit(tridentfb_exit);1835 1836MODULE_AUTHOR("Jani Monoses <jani@iv.ro>");1837MODULE_DESCRIPTION("Framebuffer driver for Trident cards");1838MODULE_LICENSE("GPL");1839MODULE_ALIAS("cyblafb");1840 1841