linux/drivers/video/fbdev/i740fb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * i740fb - framebuffer driver for Intel740
   4 * Copyright (c) 2011 Ondrej Zary
   5 *
   6 * Based on old i740fb driver (c) 2001-2002 Andrey Ulanov <drey@rt.mipt.ru>
   7 * which was partially based on:
   8 *  VGA 16-color framebuffer driver (c) 1999 Ben Pfaff <pfaffben@debian.org>
   9 *      and Petr Vandrovec <VANDROVE@vc.cvut.cz>
  10 *  i740 driver from XFree86 (c) 1998-1999 Precision Insight, Inc., Cedar Park,
  11 *      Texas.
  12 *  i740fb by Patrick LERDA, v0.9
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/kernel.h>
  17#include <linux/errno.h>
  18#include <linux/string.h>
  19#include <linux/mm.h>
  20#include <linux/slab.h>
  21#include <linux/delay.h>
  22#include <linux/fb.h>
  23#include <linux/init.h>
  24#include <linux/pci.h>
  25#include <linux/pci_ids.h>
  26#include <linux/i2c.h>
  27#include <linux/i2c-algo-bit.h>
  28#include <linux/console.h>
  29#include <video/vga.h>
  30
  31#include "i740_reg.h"
  32
  33static char *mode_option;
  34static int mtrr = 1;
  35
  36struct i740fb_par {
  37        unsigned char __iomem *regs;
  38        bool has_sgram;
  39        int wc_cookie;
  40        bool ddc_registered;
  41        struct i2c_adapter ddc_adapter;
  42        struct i2c_algo_bit_data ddc_algo;
  43        u32 pseudo_palette[16];
  44        struct mutex open_lock;
  45        unsigned int ref_count;
  46
  47        u8 crtc[VGA_CRT_C];
  48        u8 atc[VGA_ATT_C];
  49        u8 gdc[VGA_GFX_C];
  50        u8 seq[VGA_SEQ_C];
  51        u8 misc;
  52        u8 vss;
  53
  54        /* i740 specific registers */
  55        u8 display_cntl;
  56        u8 pixelpipe_cfg0;
  57        u8 pixelpipe_cfg1;
  58        u8 pixelpipe_cfg2;
  59        u8 video_clk2_m;
  60        u8 video_clk2_n;
  61        u8 video_clk2_mn_msbs;
  62        u8 video_clk2_div_sel;
  63        u8 pll_cntl;
  64        u8 address_mapping;
  65        u8 io_cntl;
  66        u8 bitblt_cntl;
  67        u8 ext_vert_total;
  68        u8 ext_vert_disp_end;
  69        u8 ext_vert_sync_start;
  70        u8 ext_vert_blank_start;
  71        u8 ext_horiz_total;
  72        u8 ext_horiz_blank;
  73        u8 ext_offset;
  74        u8 interlace_cntl;
  75        u32 lmi_fifo_watermark;
  76        u8 ext_start_addr;
  77        u8 ext_start_addr_hi;
  78};
  79
  80#define DACSPEED8       203
  81#define DACSPEED16      163
  82#define DACSPEED24_SG   136
  83#define DACSPEED24_SD   128
  84#define DACSPEED32      86
  85
  86static const struct fb_fix_screeninfo i740fb_fix = {
  87        .id =           "i740fb",
  88        .type =         FB_TYPE_PACKED_PIXELS,
  89        .visual =       FB_VISUAL_TRUECOLOR,
  90        .xpanstep =     8,
  91        .ypanstep =     1,
  92        .accel =        FB_ACCEL_NONE,
  93};
  94
  95static inline void i740outb(struct i740fb_par *par, u16 port, u8 val)
  96{
  97        vga_mm_w(par->regs, port, val);
  98}
  99static inline u8 i740inb(struct i740fb_par *par, u16 port)
 100{
 101        return vga_mm_r(par->regs, port);
 102}
 103static inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val)
 104{
 105        vga_mm_w_fast(par->regs, port, reg, val);
 106}
 107static inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg)
 108{
 109        vga_mm_w(par->regs, port, reg);
 110        return vga_mm_r(par->regs, port+1);
 111}
 112static inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg,
 113                                   u8 val, u8 mask)
 114{
 115        vga_mm_w_fast(par->regs, port, reg, (val & mask)
 116                | (i740inreg(par, port, reg) & ~mask));
 117}
 118
 119#define REG_DDC_DRIVE   0x62
 120#define REG_DDC_STATE   0x63
 121#define DDC_SCL         (1 << 3)
 122#define DDC_SDA         (1 << 2)
 123
 124static void i740fb_ddc_setscl(void *data, int val)
 125{
 126        struct i740fb_par *par = data;
 127
 128        i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL);
 129        i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL);
 130}
 131
 132static void i740fb_ddc_setsda(void *data, int val)
 133{
 134        struct i740fb_par *par = data;
 135
 136        i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA);
 137        i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA);
 138}
 139
 140static int i740fb_ddc_getscl(void *data)
 141{
 142        struct i740fb_par *par = data;
 143
 144        i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL);
 145
 146        return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL);
 147}
 148
 149static int i740fb_ddc_getsda(void *data)
 150{
 151        struct i740fb_par *par = data;
 152
 153        i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA);
 154
 155        return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA);
 156}
 157
 158static int i740fb_setup_ddc_bus(struct fb_info *info)
 159{
 160        struct i740fb_par *par = info->par;
 161
 162        strlcpy(par->ddc_adapter.name, info->fix.id,
 163                sizeof(par->ddc_adapter.name));
 164        par->ddc_adapter.owner          = THIS_MODULE;
 165        par->ddc_adapter.class          = I2C_CLASS_DDC;
 166        par->ddc_adapter.algo_data      = &par->ddc_algo;
 167        par->ddc_adapter.dev.parent     = info->device;
 168        par->ddc_algo.setsda            = i740fb_ddc_setsda;
 169        par->ddc_algo.setscl            = i740fb_ddc_setscl;
 170        par->ddc_algo.getsda            = i740fb_ddc_getsda;
 171        par->ddc_algo.getscl            = i740fb_ddc_getscl;
 172        par->ddc_algo.udelay            = 10;
 173        par->ddc_algo.timeout           = 20;
 174        par->ddc_algo.data              = par;
 175
 176        i2c_set_adapdata(&par->ddc_adapter, par);
 177
 178        return i2c_bit_add_bus(&par->ddc_adapter);
 179}
 180
 181static int i740fb_open(struct fb_info *info, int user)
 182{
 183        struct i740fb_par *par = info->par;
 184
 185        mutex_lock(&(par->open_lock));
 186        par->ref_count++;
 187        mutex_unlock(&(par->open_lock));
 188
 189        return 0;
 190}
 191
 192static int i740fb_release(struct fb_info *info, int user)
 193{
 194        struct i740fb_par *par = info->par;
 195
 196        mutex_lock(&(par->open_lock));
 197        if (par->ref_count == 0) {
 198                fb_err(info, "release called with zero refcount\n");
 199                mutex_unlock(&(par->open_lock));
 200                return -EINVAL;
 201        }
 202
 203        par->ref_count--;
 204        mutex_unlock(&(par->open_lock));
 205
 206        return 0;
 207}
 208
 209static u32 i740_calc_fifo(struct i740fb_par *par, u32 freq, int bpp)
 210{
 211        /*
 212         * Would like to calculate these values automatically, but a generic
 213         * algorithm does not seem possible.  Note: These FIFO water mark
 214         * values were tested on several cards and seem to eliminate the
 215         * all of the snow and vertical banding, but fine adjustments will
 216         * probably be required for other cards.
 217         */
 218
 219        u32 wm;
 220
 221        switch (bpp) {
 222        case 8:
 223                if      (freq > 200)
 224                        wm = 0x18120000;
 225                else if (freq > 175)
 226                        wm = 0x16110000;
 227                else if (freq > 135)
 228                        wm = 0x120E0000;
 229                else
 230                        wm = 0x100D0000;
 231                break;
 232        case 15:
 233        case 16:
 234                if (par->has_sgram) {
 235                        if      (freq > 140)
 236                                wm = 0x2C1D0000;
 237                        else if (freq > 120)
 238                                wm = 0x2C180000;
 239                        else if (freq > 100)
 240                                wm = 0x24160000;
 241                        else if (freq >  90)
 242                                wm = 0x18120000;
 243                        else if (freq >  50)
 244                                wm = 0x16110000;
 245                        else if (freq >  32)
 246                                wm = 0x13100000;
 247                        else
 248                                wm = 0x120E0000;
 249                } else {
 250                        if      (freq > 160)
 251                                wm = 0x28200000;
 252                        else if (freq > 140)
 253                                wm = 0x2A1E0000;
 254                        else if (freq > 130)
 255                                wm = 0x2B1A0000;
 256                        else if (freq > 120)
 257                                wm = 0x2C180000;
 258                        else if (freq > 100)
 259                                wm = 0x24180000;
 260                        else if (freq >  90)
 261                                wm = 0x18120000;
 262                        else if (freq >  50)
 263                                wm = 0x16110000;
 264                        else if (freq >  32)
 265                                wm = 0x13100000;
 266                        else
 267                                wm = 0x120E0000;
 268                }
 269                break;
 270        case 24:
 271                if (par->has_sgram) {
 272                        if      (freq > 130)
 273                                wm = 0x31200000;
 274                        else if (freq > 120)
 275                                wm = 0x2E200000;
 276                        else if (freq > 100)
 277                                wm = 0x2C1D0000;
 278                        else if (freq >  80)
 279                                wm = 0x25180000;
 280                        else if (freq >  64)
 281                                wm = 0x24160000;
 282                        else if (freq >  49)
 283                                wm = 0x18120000;
 284                        else if (freq >  32)
 285                                wm = 0x16110000;
 286                        else
 287                                wm = 0x13100000;
 288                } else {
 289                        if      (freq > 120)
 290                                wm = 0x311F0000;
 291                        else if (freq > 100)
 292                                wm = 0x2C1D0000;
 293                        else if (freq >  80)
 294                                wm = 0x25180000;
 295                        else if (freq >  64)
 296                                wm = 0x24160000;
 297                        else if (freq >  49)
 298                                wm = 0x18120000;
 299                        else if (freq >  32)
 300                                wm = 0x16110000;
 301                        else
 302                                wm = 0x13100000;
 303                }
 304                break;
 305        case 32:
 306                if (par->has_sgram) {
 307                        if      (freq >  80)
 308                                wm = 0x2A200000;
 309                        else if (freq >  60)
 310                                wm = 0x281A0000;
 311                        else if (freq >  49)
 312                                wm = 0x25180000;
 313                        else if (freq >  32)
 314                                wm = 0x18120000;
 315                        else
 316                                wm = 0x16110000;
 317                } else {
 318                        if      (freq >  80)
 319                                wm = 0x29200000;
 320                        else if (freq >  60)
 321                                wm = 0x281A0000;
 322                        else if (freq >  49)
 323                                wm = 0x25180000;
 324                        else if (freq >  32)
 325                                wm = 0x18120000;
 326                        else
 327                                wm = 0x16110000;
 328                }
 329                break;
 330        }
 331
 332        return wm;
 333}
 334
 335/* clock calculation from i740fb by Patrick LERDA */
 336
 337#define I740_RFREQ              1000000
 338#define TARGET_MAX_N            30
 339#define I740_FFIX               (1 << 8)
 340#define I740_RFREQ_FIX          (I740_RFREQ / I740_FFIX)
 341#define I740_REF_FREQ           (6667 * I740_FFIX / 100)        /* 66.67 MHz */
 342#define I740_MAX_VCO_FREQ       (450 * I740_FFIX)               /* 450 MHz */
 343
 344static void i740_calc_vclk(u32 freq, struct i740fb_par *par)
 345{
 346        const u32 err_max    = freq / (200  * I740_RFREQ / I740_FFIX);
 347        const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX);
 348        u32 err_best = 512 * I740_FFIX;
 349        u32 f_err, f_vco;
 350        int m_best = 0, n_best = 0, p_best = 0;
 351        int m, n;
 352
 353        p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX)));
 354        f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX;
 355        freq = freq / I740_RFREQ_FIX;
 356
 357        n = 2;
 358        do {
 359                n++;
 360                m = ((f_vco * n) / I740_REF_FREQ + 2) / 4;
 361
 362                if (m < 3)
 363                        m = 3;
 364
 365                {
 366                        u32 f_out = (((m * I740_REF_FREQ * 4)
 367                                 / n) + ((1 << p_best) / 2)) / (1 << p_best);
 368
 369                        f_err = (freq - f_out);
 370
 371                        if (abs(f_err) < err_max) {
 372                                m_best = m;
 373                                n_best = n;
 374                                err_best = f_err;
 375                        }
 376                }
 377        } while ((abs(f_err) >= err_target) &&
 378                 ((n <= TARGET_MAX_N) || (abs(err_best) > err_max)));
 379
 380        if (abs(f_err) < err_target) {
 381                m_best = m;
 382                n_best = n;
 383        }
 384
 385        par->video_clk2_m = (m_best - 2) & 0xFF;
 386        par->video_clk2_n = (n_best - 2) & 0xFF;
 387        par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS)
 388                                 | (((m_best - 2) >> 8) & VCO_M_MSBS));
 389        par->video_clk2_div_sel = ((p_best << 4) | REF_DIV_1);
 390}
 391
 392static int i740fb_decode_var(const struct fb_var_screeninfo *var,
 393                             struct i740fb_par *par, struct fb_info *info)
 394{
 395        /*
 396         * Get the video params out of 'var'.
 397         * If a value doesn't fit, round it up, if it's too big, return -EINVAL.
 398         */
 399
 400        u32 xres, right, hslen, left, xtotal;
 401        u32 yres, lower, vslen, upper, ytotal;
 402        u32 vxres, xoffset, vyres, yoffset;
 403        u32 bpp, base, dacspeed24, mem;
 404        u8 r7;
 405        int i;
 406
 407        dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n",
 408                  var->xres, var->yres, var->xres_virtual, var->xres_virtual);
 409        dev_dbg(info->device, " xoff: %i, yoff: %i, bpp: %i, graysc: %i\n",
 410                  var->xoffset, var->yoffset, var->bits_per_pixel,
 411                  var->grayscale);
 412        dev_dbg(info->device, " activate: %i, nonstd: %i, vmode: %i\n",
 413                  var->activate, var->nonstd, var->vmode);
 414        dev_dbg(info->device, " pixclock: %i, hsynclen:%i, vsynclen:%i\n",
 415                  var->pixclock, var->hsync_len, var->vsync_len);
 416        dev_dbg(info->device, " left: %i, right: %i, up:%i, lower:%i\n",
 417                  var->left_margin, var->right_margin, var->upper_margin,
 418                  var->lower_margin);
 419
 420
 421        bpp = var->bits_per_pixel;
 422        switch (bpp) {
 423        case 1 ... 8:
 424                bpp = 8;
 425                if ((1000000 / var->pixclock) > DACSPEED8) {
 426                        dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n",
 427                                1000000 / var->pixclock, DACSPEED8);
 428                        return -EINVAL;
 429                }
 430                break;
 431        case 9 ... 15:
 432                bpp = 15;
 433                fallthrough;
 434        case 16:
 435                if ((1000000 / var->pixclock) > DACSPEED16) {
 436                        dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n",
 437                                1000000 / var->pixclock, DACSPEED16);
 438                        return -EINVAL;
 439                }
 440                break;
 441        case 17 ... 24:
 442                bpp = 24;
 443                dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD;
 444                if ((1000000 / var->pixclock) > dacspeed24) {
 445                        dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n",
 446                                1000000 / var->pixclock, dacspeed24);
 447                        return -EINVAL;
 448                }
 449                break;
 450        case 25 ... 32:
 451                bpp = 32;
 452                if ((1000000 / var->pixclock) > DACSPEED32) {
 453                        dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n",
 454                                1000000 / var->pixclock, DACSPEED32);
 455                        return -EINVAL;
 456                }
 457                break;
 458        default:
 459                return -EINVAL;
 460        }
 461
 462        xres = ALIGN(var->xres, 8);
 463        vxres = ALIGN(var->xres_virtual, 16);
 464        if (vxres < xres)
 465                vxres = xres;
 466
 467        xoffset = ALIGN(var->xoffset, 8);
 468        if (xres + xoffset > vxres)
 469                xoffset = vxres - xres;
 470
 471        left = ALIGN(var->left_margin, 8);
 472        right = ALIGN(var->right_margin, 8);
 473        hslen = ALIGN(var->hsync_len, 8);
 474
 475        yres = var->yres;
 476        vyres = var->yres_virtual;
 477        if (yres > vyres)
 478                vyres = yres;
 479
 480        yoffset = var->yoffset;
 481        if (yres + yoffset > vyres)
 482                yoffset = vyres - yres;
 483
 484        lower = var->lower_margin;
 485        vslen = var->vsync_len;
 486        upper = var->upper_margin;
 487
 488        mem = vxres * vyres * ((bpp + 1) / 8);
 489        if (mem > info->screen_size) {
 490                dev_err(info->device, "not enough video memory (%d KB requested, %ld KB available)\n",
 491                        mem >> 10, info->screen_size >> 10);
 492                return -ENOMEM;
 493        }
 494
 495        if (yoffset + yres > vyres)
 496                yoffset = vyres - yres;
 497
 498        xtotal = xres + right + hslen + left;
 499        ytotal = yres + lower + vslen + upper;
 500
 501        par->crtc[VGA_CRTC_H_TOTAL] = (xtotal >> 3) - 5;
 502        par->crtc[VGA_CRTC_H_DISP] = (xres >> 3) - 1;
 503        par->crtc[VGA_CRTC_H_BLANK_START] = ((xres + right) >> 3) - 1;
 504        par->crtc[VGA_CRTC_H_SYNC_START] = (xres + right) >> 3;
 505        par->crtc[VGA_CRTC_H_SYNC_END] = (((xres + right + hslen) >> 3) & 0x1F)
 506                | ((((xres + right + hslen) >> 3) & 0x20) << 2);
 507        par->crtc[VGA_CRTC_H_BLANK_END] = ((xres + right + hslen) >> 3 & 0x1F)
 508                | 0x80;
 509
 510        par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2;
 511
 512        r7 = 0x10;      /* disable linecompare */
 513        if (ytotal & 0x100)
 514                r7 |= 0x01;
 515        if (ytotal & 0x200)
 516                r7 |= 0x20;
 517
 518        par->crtc[VGA_CRTC_PRESET_ROW] = 0;
 519        par->crtc[VGA_CRTC_MAX_SCAN] = 0x40;    /* 1 scanline, no linecmp */
 520        if (var->vmode & FB_VMODE_DOUBLE)
 521                par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80;
 522        par->crtc[VGA_CRTC_CURSOR_START] = 0x00;
 523        par->crtc[VGA_CRTC_CURSOR_END] = 0x00;
 524        par->crtc[VGA_CRTC_CURSOR_HI] = 0x00;
 525        par->crtc[VGA_CRTC_CURSOR_LO] = 0x00;
 526        par->crtc[VGA_CRTC_V_DISP_END] = yres-1;
 527        if ((yres-1) & 0x100)
 528                r7 |= 0x02;
 529        if ((yres-1) & 0x200)
 530                r7 |= 0x40;
 531
 532        par->crtc[VGA_CRTC_V_BLANK_START] = yres + lower - 1;
 533        par->crtc[VGA_CRTC_V_SYNC_START] = yres + lower - 1;
 534        if ((yres + lower - 1) & 0x100)
 535                r7 |= 0x0C;
 536        if ((yres + lower - 1) & 0x200) {
 537                par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20;
 538                r7 |= 0x80;
 539        }
 540
 541        /* disabled IRQ */
 542        par->crtc[VGA_CRTC_V_SYNC_END] =
 543                ((yres + lower - 1 + vslen) & 0x0F) & ~0x10;
 544        /* 0x7F for VGA, but some SVGA chips require all 8 bits to be set */
 545        par->crtc[VGA_CRTC_V_BLANK_END] = (yres + lower - 1 + vslen) & 0xFF;
 546
 547        par->crtc[VGA_CRTC_UNDERLINE] = 0x00;
 548        par->crtc[VGA_CRTC_MODE] = 0xC3 ;
 549        par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF;
 550        par->crtc[VGA_CRTC_OVERFLOW] = r7;
 551
 552        par->vss = 0x00;        /* 3DA */
 553
 554        for (i = 0x00; i < 0x10; i++)
 555                par->atc[i] = i;
 556        par->atc[VGA_ATC_MODE] = 0x81;
 557        par->atc[VGA_ATC_OVERSCAN] = 0x00;      /* 0 for EGA, 0xFF for VGA */
 558        par->atc[VGA_ATC_PLANE_ENABLE] = 0x0F;
 559        par->atc[VGA_ATC_COLOR_PAGE] = 0x00;
 560
 561        par->misc = 0xC3;
 562        if (var->sync & FB_SYNC_HOR_HIGH_ACT)
 563                par->misc &= ~0x40;
 564        if (var->sync & FB_SYNC_VERT_HIGH_ACT)
 565                par->misc &= ~0x80;
 566
 567        par->seq[VGA_SEQ_CLOCK_MODE] = 0x01;
 568        par->seq[VGA_SEQ_PLANE_WRITE] = 0x0F;
 569        par->seq[VGA_SEQ_CHARACTER_MAP] = 0x00;
 570        par->seq[VGA_SEQ_MEMORY_MODE] = 0x06;
 571
 572        par->gdc[VGA_GFX_SR_VALUE] = 0x00;
 573        par->gdc[VGA_GFX_SR_ENABLE] = 0x00;
 574        par->gdc[VGA_GFX_COMPARE_VALUE] = 0x00;
 575        par->gdc[VGA_GFX_DATA_ROTATE] = 0x00;
 576        par->gdc[VGA_GFX_PLANE_READ] = 0;
 577        par->gdc[VGA_GFX_MODE] = 0x02;
 578        par->gdc[VGA_GFX_MISC] = 0x05;
 579        par->gdc[VGA_GFX_COMPARE_MASK] = 0x0F;
 580        par->gdc[VGA_GFX_BIT_MASK] = 0xFF;
 581
 582        base = (yoffset * vxres + (xoffset & ~7)) >> 2;
 583        switch (bpp) {
 584        case 8:
 585                par->crtc[VGA_CRTC_OFFSET] = vxres >> 3;
 586                par->ext_offset = vxres >> 11;
 587                par->pixelpipe_cfg1 = DISPLAY_8BPP_MODE;
 588                par->bitblt_cntl = COLEXP_8BPP;
 589                break;
 590        case 15: /* 0rrrrrgg gggbbbbb */
 591        case 16: /* rrrrrggg gggbbbbb */
 592                par->pixelpipe_cfg1 = (var->green.length == 6) ?
 593                        DISPLAY_16BPP_MODE : DISPLAY_15BPP_MODE;
 594                par->crtc[VGA_CRTC_OFFSET] = vxres >> 2;
 595                par->ext_offset = vxres >> 10;
 596                par->bitblt_cntl = COLEXP_16BPP;
 597                base *= 2;
 598                break;
 599        case 24:
 600                par->crtc[VGA_CRTC_OFFSET] = (vxres * 3) >> 3;
 601                par->ext_offset = (vxres * 3) >> 11;
 602                par->pixelpipe_cfg1 = DISPLAY_24BPP_MODE;
 603                par->bitblt_cntl = COLEXP_24BPP;
 604                base &= 0xFFFFFFFE; /* ...ignore the last bit. */
 605                base *= 3;
 606                break;
 607        case 32:
 608                par->crtc[VGA_CRTC_OFFSET] = vxres >> 1;
 609                par->ext_offset = vxres >> 9;
 610                par->pixelpipe_cfg1 = DISPLAY_32BPP_MODE;
 611                par->bitblt_cntl = COLEXP_RESERVED; /* Unimplemented on i740 */
 612                base *= 4;
 613                break;
 614        }
 615
 616        par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF;
 617        par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >>  8;
 618        par->ext_start_addr =
 619                ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE;
 620        par->ext_start_addr_hi = (base & 0x3FC00000) >> 22;
 621
 622        par->pixelpipe_cfg0 = DAC_8_BIT;
 623
 624        par->pixelpipe_cfg2 = DISPLAY_GAMMA_ENABLE | OVERLAY_GAMMA_ENABLE;
 625        par->io_cntl = EXTENDED_CRTC_CNTL;
 626        par->address_mapping = LINEAR_MODE_ENABLE | PAGE_MAPPING_ENABLE;
 627        par->display_cntl = HIRES_MODE;
 628
 629        /* Set the MCLK freq */
 630        par->pll_cntl = PLL_MEMCLK_100000KHZ; /* 100 MHz -- use as default */
 631
 632        /* Calculate the extended CRTC regs */
 633        par->ext_vert_total = (ytotal - 2) >> 8;
 634        par->ext_vert_disp_end = (yres - 1) >> 8;
 635        par->ext_vert_sync_start = (yres + lower) >> 8;
 636        par->ext_vert_blank_start = (yres + lower) >> 8;
 637        par->ext_horiz_total = ((xtotal >> 3) - 5) >> 8;
 638        par->ext_horiz_blank = (((xres + right) >> 3) & 0x40) >> 6;
 639
 640        par->interlace_cntl = INTERLACE_DISABLE;
 641
 642        /* Set the overscan color to 0. (NOTE: This only affects >8bpp mode) */
 643        par->atc[VGA_ATC_OVERSCAN] = 0;
 644
 645        /* Calculate VCLK that most closely matches the requested dot clock */
 646        i740_calc_vclk((((u32)1e9) / var->pixclock) * (u32)(1e3), par);
 647
 648        /* Since we program the clocks ourselves, always use VCLK2. */
 649        par->misc |= 0x0C;
 650
 651        /* Calculate the FIFO Watermark and Burst Length. */
 652        par->lmi_fifo_watermark =
 653                i740_calc_fifo(par, 1000000 / var->pixclock, bpp);
 654
 655        return 0;
 656}
 657
 658static int i740fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 659{
 660        switch (var->bits_per_pixel) {
 661        case 8:
 662                var->red.offset = var->green.offset = var->blue.offset = 0;
 663                var->red.length = var->green.length = var->blue.length = 8;
 664                break;
 665        case 16:
 666                switch (var->green.length) {
 667                default:
 668                case 5:
 669                        var->red.offset = 10;
 670                        var->green.offset = 5;
 671                        var->blue.offset = 0;
 672                        var->red.length = 5;
 673                        var->green.length = 5;
 674                        var->blue.length = 5;
 675                        break;
 676                case 6:
 677                        var->red.offset = 11;
 678                        var->green.offset = 5;
 679                        var->blue.offset = 0;
 680                        var->red.length = var->blue.length = 5;
 681                        break;
 682                }
 683                break;
 684        case 24:
 685                var->red.offset = 16;
 686                var->green.offset = 8;
 687                var->blue.offset = 0;
 688                var->red.length = var->green.length = var->blue.length = 8;
 689                break;
 690        case 32:
 691                var->transp.offset = 24;
 692                var->red.offset = 16;
 693                var->green.offset = 8;
 694                var->blue.offset = 0;
 695                var->transp.length = 8;
 696                var->red.length = var->green.length = var->blue.length = 8;
 697                break;
 698        default:
 699                return -EINVAL;
 700        }
 701
 702        if (var->xres > var->xres_virtual)
 703                var->xres_virtual = var->xres;
 704
 705        if (var->yres > var->yres_virtual)
 706                var->yres_virtual = var->yres;
 707
 708        if (info->monspecs.hfmax && info->monspecs.vfmax &&
 709            info->monspecs.dclkmax && fb_validate_mode(var, info) < 0)
 710                return -EINVAL;
 711
 712        return 0;
 713}
 714
 715static void vga_protect(struct i740fb_par *par)
 716{
 717        /* disable the display */
 718        i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0x20, 0x20);
 719
 720        i740inb(par, 0x3DA);
 721        i740outb(par, VGA_ATT_W, 0x00); /* enable palette access */
 722}
 723
 724static void vga_unprotect(struct i740fb_par *par)
 725{
 726        /* reenable display */
 727        i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0, 0x20);
 728
 729        i740inb(par, 0x3DA);
 730        i740outb(par, VGA_ATT_W, 0x20); /* disable palette access */
 731}
 732
 733static int i740fb_set_par(struct fb_info *info)
 734{
 735        struct i740fb_par *par = info->par;
 736        u32 itemp;
 737        int i;
 738
 739        i = i740fb_decode_var(&info->var, par, info);
 740        if (i)
 741                return i;
 742
 743        memset(info->screen_base, 0, info->screen_size);
 744
 745        vga_protect(par);
 746
 747        i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_DISABLE);
 748
 749        mdelay(1);
 750
 751        i740outreg(par, XRX, VCLK2_VCO_M, par->video_clk2_m);
 752        i740outreg(par, XRX, VCLK2_VCO_N, par->video_clk2_n);
 753        i740outreg(par, XRX, VCLK2_VCO_MN_MSBS, par->video_clk2_mn_msbs);
 754        i740outreg(par, XRX, VCLK2_VCO_DIV_SEL, par->video_clk2_div_sel);
 755
 756        i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0,
 757                        par->pixelpipe_cfg0 & DAC_8_BIT, 0x80);
 758
 759        i740inb(par, 0x3DA);
 760        i740outb(par, 0x3C0, 0x00);
 761
 762        /* update misc output register */
 763        i740outb(par, VGA_MIS_W, par->misc | 0x01);
 764
 765        /* synchronous reset on */
 766        i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x01);
 767        /* write sequencer registers */
 768        i740outreg(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE,
 769                        par->seq[VGA_SEQ_CLOCK_MODE] | 0x20);
 770        for (i = 2; i < VGA_SEQ_C; i++)
 771                i740outreg(par, VGA_SEQ_I, i, par->seq[i]);
 772
 773        /* synchronous reset off */
 774        i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x03);
 775
 776        /* deprotect CRT registers 0-7 */
 777        i740outreg(par, VGA_CRT_IC, VGA_CRTC_V_SYNC_END,
 778                        par->crtc[VGA_CRTC_V_SYNC_END]);
 779
 780        /* write CRT registers */
 781        for (i = 0; i < VGA_CRT_C; i++)
 782                i740outreg(par, VGA_CRT_IC, i, par->crtc[i]);
 783
 784        /* write graphics controller registers */
 785        for (i = 0; i < VGA_GFX_C; i++)
 786                i740outreg(par, VGA_GFX_I, i, par->gdc[i]);
 787
 788        /* write attribute controller registers */
 789        for (i = 0; i < VGA_ATT_C; i++) {
 790                i740inb(par, VGA_IS1_RC);               /* reset flip-flop */
 791                i740outb(par, VGA_ATT_IW, i);
 792                i740outb(par, VGA_ATT_IW, par->atc[i]);
 793        }
 794
 795        i740inb(par, VGA_IS1_RC);
 796        i740outb(par, VGA_ATT_IW, 0x20);
 797
 798        i740outreg(par, VGA_CRT_IC, EXT_VERT_TOTAL, par->ext_vert_total);
 799        i740outreg(par, VGA_CRT_IC, EXT_VERT_DISPLAY, par->ext_vert_disp_end);
 800        i740outreg(par, VGA_CRT_IC, EXT_VERT_SYNC_START,
 801                        par->ext_vert_sync_start);
 802        i740outreg(par, VGA_CRT_IC, EXT_VERT_BLANK_START,
 803                        par->ext_vert_blank_start);
 804        i740outreg(par, VGA_CRT_IC, EXT_HORIZ_TOTAL, par->ext_horiz_total);
 805        i740outreg(par, VGA_CRT_IC, EXT_HORIZ_BLANK, par->ext_horiz_blank);
 806        i740outreg(par, VGA_CRT_IC, EXT_OFFSET, par->ext_offset);
 807        i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, par->ext_start_addr_hi);
 808        i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, par->ext_start_addr);
 809
 810        i740outreg_mask(par, VGA_CRT_IC, INTERLACE_CNTL,
 811                        par->interlace_cntl, INTERLACE_ENABLE);
 812        i740outreg_mask(par, XRX, ADDRESS_MAPPING, par->address_mapping, 0x1F);
 813        i740outreg_mask(par, XRX, BITBLT_CNTL, par->bitblt_cntl, COLEXP_MODE);
 814        i740outreg_mask(par, XRX, DISPLAY_CNTL,
 815                        par->display_cntl, VGA_WRAP_MODE | GUI_MODE);
 816        i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, par->pixelpipe_cfg0, 0x9B);
 817        i740outreg_mask(par, XRX, PIXPIPE_CONFIG_2, par->pixelpipe_cfg2, 0x0C);
 818
 819        i740outreg(par, XRX, PLL_CNTL, par->pll_cntl);
 820
 821        i740outreg_mask(par, XRX, PIXPIPE_CONFIG_1,
 822                        par->pixelpipe_cfg1, DISPLAY_COLOR_MODE);
 823
 824        itemp = readl(par->regs + FWATER_BLC);
 825        itemp &= ~(LMI_BURST_LENGTH | LMI_FIFO_WATERMARK);
 826        itemp |= par->lmi_fifo_watermark;
 827        writel(itemp, par->regs + FWATER_BLC);
 828
 829        i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_60HZ);
 830
 831        i740outreg_mask(par, MRX, COL_KEY_CNTL_1, 0, BLANK_DISP_OVERLAY);
 832        i740outreg_mask(par, XRX, IO_CTNL,
 833                        par->io_cntl, EXTENDED_ATTR_CNTL | EXTENDED_CRTC_CNTL);
 834
 835        if (par->pixelpipe_cfg1 != DISPLAY_8BPP_MODE) {
 836                i740outb(par, VGA_PEL_MSK, 0xFF);
 837                i740outb(par, VGA_PEL_IW, 0x00);
 838                for (i = 0; i < 256; i++) {
 839                        itemp = (par->pixelpipe_cfg0 & DAC_8_BIT) ? i : i >> 2;
 840                        i740outb(par, VGA_PEL_D, itemp);
 841                        i740outb(par, VGA_PEL_D, itemp);
 842                        i740outb(par, VGA_PEL_D, itemp);
 843                }
 844        }
 845
 846        /* Wait for screen to stabilize. */
 847        mdelay(50);
 848        vga_unprotect(par);
 849
 850        info->fix.line_length =
 851                        info->var.xres_virtual * info->var.bits_per_pixel / 8;
 852        if (info->var.bits_per_pixel == 8)
 853                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 854        else
 855                info->fix.visual = FB_VISUAL_TRUECOLOR;
 856
 857        return 0;
 858}
 859
 860static int i740fb_setcolreg(unsigned regno, unsigned red, unsigned green,
 861                           unsigned blue, unsigned transp,
 862                           struct fb_info *info)
 863{
 864        u32 r, g, b;
 865
 866        dev_dbg(info->device, "setcolreg: regno: %i, red=%d, green=%d, blue=%d, transp=%d, bpp=%d\n",
 867                regno, red, green, blue, transp, info->var.bits_per_pixel);
 868
 869        switch (info->fix.visual) {
 870        case FB_VISUAL_PSEUDOCOLOR:
 871                if (regno >= 256)
 872                        return -EINVAL;
 873                i740outb(info->par, VGA_PEL_IW, regno);
 874                i740outb(info->par, VGA_PEL_D, red >> 8);
 875                i740outb(info->par, VGA_PEL_D, green >> 8);
 876                i740outb(info->par, VGA_PEL_D, blue >> 8);
 877                break;
 878        case FB_VISUAL_TRUECOLOR:
 879                if (regno >= 16)
 880                        return -EINVAL;
 881                r = (red >> (16 - info->var.red.length))
 882                        << info->var.red.offset;
 883                b = (blue >> (16 - info->var.blue.length))
 884                        << info->var.blue.offset;
 885                g = (green >> (16 - info->var.green.length))
 886                        << info->var.green.offset;
 887                ((u32 *) info->pseudo_palette)[regno] = r | g | b;
 888                break;
 889        default:
 890                return -EINVAL;
 891        }
 892
 893        return 0;
 894}
 895
 896static int i740fb_pan_display(struct fb_var_screeninfo *var,
 897                                 struct fb_info *info)
 898{
 899        struct i740fb_par *par = info->par;
 900        u32 base = (var->yoffset * info->var.xres_virtual
 901                 + (var->xoffset & ~7)) >> 2;
 902
 903        dev_dbg(info->device, "pan_display: xoffset: %i yoffset: %i base: %i\n",
 904                var->xoffset, var->yoffset, base);
 905
 906        switch (info->var.bits_per_pixel) {
 907        case 8:
 908                break;
 909        case 15:
 910        case 16:
 911                base *= 2;
 912                break;
 913        case 24:
 914                /*
 915                 * The last bit does not seem to have any effect on the start
 916                 * address register in 24bpp mode, so...
 917                 */
 918                base &= 0xFFFFFFFE; /* ...ignore the last bit. */
 919                base *= 3;
 920                break;
 921        case 32:
 922                base *= 4;
 923                break;
 924        }
 925
 926        par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF;
 927        par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >>  8;
 928        par->ext_start_addr_hi = (base & 0x3FC00000) >> 22;
 929        par->ext_start_addr =
 930                        ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE;
 931
 932        i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_LO,  base & 0x000000FF);
 933        i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_HI,
 934                        (base & 0x0000FF00) >> 8);
 935        i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI,
 936                        (base & 0x3FC00000) >> 22);
 937        i740outreg(par, VGA_CRT_IC, EXT_START_ADDR,
 938                        ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE);
 939
 940        return 0;
 941}
 942
 943static int i740fb_blank(int blank_mode, struct fb_info *info)
 944{
 945        struct i740fb_par *par = info->par;
 946
 947        unsigned char SEQ01;
 948        int DPMSSyncSelect;
 949
 950        switch (blank_mode) {
 951        case FB_BLANK_UNBLANK:
 952        case FB_BLANK_NORMAL:
 953                SEQ01 = 0x00;
 954                DPMSSyncSelect = HSYNC_ON | VSYNC_ON;
 955                break;
 956        case FB_BLANK_VSYNC_SUSPEND:
 957                SEQ01 = 0x20;
 958                DPMSSyncSelect = HSYNC_ON | VSYNC_OFF;
 959                break;
 960        case FB_BLANK_HSYNC_SUSPEND:
 961                SEQ01 = 0x20;
 962                DPMSSyncSelect = HSYNC_OFF | VSYNC_ON;
 963                break;
 964        case FB_BLANK_POWERDOWN:
 965                SEQ01 = 0x20;
 966                DPMSSyncSelect = HSYNC_OFF | VSYNC_OFF;
 967                break;
 968        default:
 969                return -EINVAL;
 970        }
 971        /* Turn the screen on/off */
 972        i740outb(par, SRX, 0x01);
 973        SEQ01 |= i740inb(par, SRX + 1) & ~0x20;
 974        i740outb(par, SRX, 0x01);
 975        i740outb(par, SRX + 1, SEQ01);
 976
 977        /* Set the DPMS mode */
 978        i740outreg(par, XRX, DPMS_SYNC_SELECT, DPMSSyncSelect);
 979
 980        /* Let fbcon do a soft blank for us */
 981        return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
 982}
 983
 984static const struct fb_ops i740fb_ops = {
 985        .owner          = THIS_MODULE,
 986        .fb_open        = i740fb_open,
 987        .fb_release     = i740fb_release,
 988        .fb_check_var   = i740fb_check_var,
 989        .fb_set_par     = i740fb_set_par,
 990        .fb_setcolreg   = i740fb_setcolreg,
 991        .fb_blank       = i740fb_blank,
 992        .fb_pan_display = i740fb_pan_display,
 993        .fb_fillrect    = cfb_fillrect,
 994        .fb_copyarea    = cfb_copyarea,
 995        .fb_imageblit   = cfb_imageblit,
 996};
 997
 998/* ------------------------------------------------------------------------- */
 999
1000static int i740fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
1001{
1002        struct fb_info *info;
1003        struct i740fb_par *par;
1004        int ret, tmp;
1005        bool found = false;
1006        u8 *edid;
1007
1008        info = framebuffer_alloc(sizeof(struct i740fb_par), &(dev->dev));
1009        if (!info)
1010                return -ENOMEM;
1011
1012        par = info->par;
1013        mutex_init(&par->open_lock);
1014
1015        info->var.activate = FB_ACTIVATE_NOW;
1016        info->var.bits_per_pixel = 8;
1017        info->fbops = &i740fb_ops;
1018        info->pseudo_palette = par->pseudo_palette;
1019
1020        ret = pci_enable_device(dev);
1021        if (ret) {
1022                dev_err(info->device, "cannot enable PCI device\n");
1023                goto err_enable_device;
1024        }
1025
1026        ret = pci_request_regions(dev, info->fix.id);
1027        if (ret) {
1028                dev_err(info->device, "error requesting regions\n");
1029                goto err_request_regions;
1030        }
1031
1032        info->screen_base = pci_ioremap_wc_bar(dev, 0);
1033        if (!info->screen_base) {
1034                dev_err(info->device, "error remapping base\n");
1035                ret = -ENOMEM;
1036                goto err_ioremap_1;
1037        }
1038
1039        par->regs = pci_ioremap_bar(dev, 1);
1040        if (!par->regs) {
1041                dev_err(info->device, "error remapping MMIO\n");
1042                ret = -ENOMEM;
1043                goto err_ioremap_2;
1044        }
1045
1046        /* detect memory size */
1047        if ((i740inreg(par, XRX, DRAM_ROW_TYPE) & DRAM_ROW_1)
1048                                                        == DRAM_ROW_1_SDRAM)
1049                i740outb(par, XRX, DRAM_ROW_BNDRY_1);
1050        else
1051                i740outb(par, XRX, DRAM_ROW_BNDRY_0);
1052        info->screen_size = i740inb(par, XRX + 1) * 1024 * 1024;
1053        /* detect memory type */
1054        tmp = i740inreg(par, XRX, DRAM_ROW_CNTL_LO);
1055        par->has_sgram = !((tmp & DRAM_RAS_TIMING) ||
1056                           (tmp & DRAM_RAS_PRECHARGE));
1057
1058        fb_info(info, "Intel740 on %s, %ld KB %s\n",
1059                pci_name(dev), info->screen_size >> 10,
1060                par->has_sgram ? "SGRAM" : "SDRAM");
1061
1062        info->fix = i740fb_fix;
1063        info->fix.mmio_start = pci_resource_start(dev, 1);
1064        info->fix.mmio_len = pci_resource_len(dev, 1);
1065        info->fix.smem_start = pci_resource_start(dev, 0);
1066        info->fix.smem_len = info->screen_size;
1067        info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
1068
1069        if (i740fb_setup_ddc_bus(info) == 0) {
1070                par->ddc_registered = true;
1071                edid = fb_ddc_read(&par->ddc_adapter);
1072                if (edid) {
1073                        fb_edid_to_monspecs(edid, &info->monspecs);
1074                        kfree(edid);
1075                        if (!info->monspecs.modedb)
1076                                dev_err(info->device,
1077                                        "error getting mode database\n");
1078                        else {
1079                                const struct fb_videomode *m;
1080
1081                                fb_videomode_to_modelist(
1082                                        info->monspecs.modedb,
1083                                        info->monspecs.modedb_len,
1084                                        &info->modelist);
1085                                m = fb_find_best_display(&info->monspecs,
1086                                                         &info->modelist);
1087                                if (m) {
1088                                        fb_videomode_to_var(&info->var, m);
1089                                        /* fill all other info->var's fields */
1090                                        if (!i740fb_check_var(&info->var, info))
1091                                                found = true;
1092                                }
1093                        }
1094                }
1095        }
1096
1097        if (!mode_option && !found)
1098                mode_option = "640x480-8@60";
1099
1100        if (mode_option) {
1101                ret = fb_find_mode(&info->var, info, mode_option,
1102                                   info->monspecs.modedb,
1103                                   info->monspecs.modedb_len,
1104                                   NULL, info->var.bits_per_pixel);
1105                if (!ret || ret == 4) {
1106                        dev_err(info->device, "mode %s not found\n",
1107                                mode_option);
1108                        ret = -EINVAL;
1109                }
1110        }
1111
1112        fb_destroy_modedb(info->monspecs.modedb);
1113        info->monspecs.modedb = NULL;
1114
1115        /* maximize virtual vertical size for fast scrolling */
1116        info->var.yres_virtual = info->fix.smem_len * 8 /
1117                        (info->var.bits_per_pixel * info->var.xres_virtual);
1118
1119        if (ret == -EINVAL)
1120                goto err_find_mode;
1121
1122        ret = fb_alloc_cmap(&info->cmap, 256, 0);
1123        if (ret) {
1124                dev_err(info->device, "cannot allocate colormap\n");
1125                goto err_alloc_cmap;
1126        }
1127
1128        ret = register_framebuffer(info);
1129        if (ret) {
1130                dev_err(info->device, "error registering framebuffer\n");
1131                goto err_reg_framebuffer;
1132        }
1133
1134        fb_info(info, "%s frame buffer device\n", info->fix.id);
1135        pci_set_drvdata(dev, info);
1136        if (mtrr)
1137                par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
1138                                                  info->fix.smem_len);
1139        return 0;
1140
1141err_reg_framebuffer:
1142        fb_dealloc_cmap(&info->cmap);
1143err_alloc_cmap:
1144err_find_mode:
1145        if (par->ddc_registered)
1146                i2c_del_adapter(&par->ddc_adapter);
1147        pci_iounmap(dev, par->regs);
1148err_ioremap_2:
1149        pci_iounmap(dev, info->screen_base);
1150err_ioremap_1:
1151        pci_release_regions(dev);
1152err_request_regions:
1153/*      pci_disable_device(dev); */
1154err_enable_device:
1155        framebuffer_release(info);
1156        return ret;
1157}
1158
1159static void i740fb_remove(struct pci_dev *dev)
1160{
1161        struct fb_info *info = pci_get_drvdata(dev);
1162
1163        if (info) {
1164                struct i740fb_par *par = info->par;
1165                arch_phys_wc_del(par->wc_cookie);
1166                unregister_framebuffer(info);
1167                fb_dealloc_cmap(&info->cmap);
1168                if (par->ddc_registered)
1169                        i2c_del_adapter(&par->ddc_adapter);
1170                pci_iounmap(dev, par->regs);
1171                pci_iounmap(dev, info->screen_base);
1172                pci_release_regions(dev);
1173/*              pci_disable_device(dev); */
1174                framebuffer_release(info);
1175        }
1176}
1177
1178#ifdef CONFIG_PM
1179static int i740fb_suspend(struct pci_dev *dev, pm_message_t state)
1180{
1181        struct fb_info *info = pci_get_drvdata(dev);
1182        struct i740fb_par *par = info->par;
1183
1184        /* don't disable console during hibernation and wakeup from it */
1185        if (state.event == PM_EVENT_FREEZE || state.event == PM_EVENT_PRETHAW)
1186                return 0;
1187
1188        console_lock();
1189        mutex_lock(&(par->open_lock));
1190
1191        /* do nothing if framebuffer is not active */
1192        if (par->ref_count == 0) {
1193                mutex_unlock(&(par->open_lock));
1194                console_unlock();
1195                return 0;
1196        }
1197
1198        fb_set_suspend(info, 1);
1199
1200        pci_save_state(dev);
1201        pci_disable_device(dev);
1202        pci_set_power_state(dev, pci_choose_state(dev, state));
1203
1204        mutex_unlock(&(par->open_lock));
1205        console_unlock();
1206
1207        return 0;
1208}
1209
1210static int i740fb_resume(struct pci_dev *dev)
1211{
1212        struct fb_info *info = pci_get_drvdata(dev);
1213        struct i740fb_par *par = info->par;
1214
1215        console_lock();
1216        mutex_lock(&(par->open_lock));
1217
1218        if (par->ref_count == 0)
1219                goto fail;
1220
1221        pci_set_power_state(dev, PCI_D0);
1222        pci_restore_state(dev);
1223        if (pci_enable_device(dev))
1224                goto fail;
1225
1226        i740fb_set_par(info);
1227        fb_set_suspend(info, 0);
1228
1229fail:
1230        mutex_unlock(&(par->open_lock));
1231        console_unlock();
1232        return 0;
1233}
1234#else
1235#define i740fb_suspend NULL
1236#define i740fb_resume NULL
1237#endif /* CONFIG_PM */
1238
1239#define I740_ID_PCI 0x00d1
1240#define I740_ID_AGP 0x7800
1241
1242static const struct pci_device_id i740fb_id_table[] = {
1243        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_PCI) },
1244        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_AGP) },
1245        { 0 }
1246};
1247MODULE_DEVICE_TABLE(pci, i740fb_id_table);
1248
1249static struct pci_driver i740fb_driver = {
1250        .name           = "i740fb",
1251        .id_table       = i740fb_id_table,
1252        .probe          = i740fb_probe,
1253        .remove         = i740fb_remove,
1254        .suspend        = i740fb_suspend,
1255        .resume         = i740fb_resume,
1256};
1257
1258#ifndef MODULE
1259static int  __init i740fb_setup(char *options)
1260{
1261        char *opt;
1262
1263        if (!options || !*options)
1264                return 0;
1265
1266        while ((opt = strsep(&options, ",")) != NULL) {
1267                if (!*opt)
1268                        continue;
1269                else if (!strncmp(opt, "mtrr:", 5))
1270                        mtrr = simple_strtoul(opt + 5, NULL, 0);
1271                else
1272                        mode_option = opt;
1273        }
1274
1275        return 0;
1276}
1277#endif
1278
1279static int __init i740fb_init(void)
1280{
1281#ifndef MODULE
1282        char *option = NULL;
1283
1284        if (fb_get_options("i740fb", &option))
1285                return -ENODEV;
1286        i740fb_setup(option);
1287#endif
1288
1289        return pci_register_driver(&i740fb_driver);
1290}
1291
1292static void __exit i740fb_exit(void)
1293{
1294        pci_unregister_driver(&i740fb_driver);
1295}
1296
1297module_init(i740fb_init);
1298module_exit(i740fb_exit);
1299
1300MODULE_AUTHOR("(c) 2011 Ondrej Zary <linux@rainbow-software.org>");
1301MODULE_LICENSE("GPL");
1302MODULE_DESCRIPTION("fbdev driver for Intel740");
1303
1304module_param(mode_option, charp, 0444);
1305MODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)");
1306
1307module_param(mtrr, int, 0444);
1308MODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)");
1309