File Index
Files and directories
Sign in
FILE

i740fb.c

File preview with syntax highlighting.

linux-6.19.14/drivers/video/fbdev/i740fb.c C
   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/aperture.h>
  16#include <linux/module.h>
  17#include <linux/kernel.h>
  18#include <linux/errno.h>
  19#include <linux/string.h>
  20#include <linux/mm.h>
  21#include <linux/slab.h>
  22#include <linux/delay.h>
  23#include <linux/fb.h>
  24#include <linux/init.h>
  25#include <linux/pci.h>
  26#include <linux/pci_ids.h>
  27#include <linux/i2c.h>
  28#include <linux/i2c-algo-bit.h>
  29#include <linux/console.h>
  30#include <video/vga.h>
  31
  32#include "i740_reg.h"
  33
  34static char *mode_option;
  35static int mtrr = 1;
  36
  37struct i740fb_par {
  38	unsigned char __iomem *regs;
  39	bool has_sgram;
  40	int wc_cookie;
  41	bool ddc_registered;
  42	struct i2c_adapter ddc_adapter;
  43	struct i2c_algo_bit_data ddc_algo;
  44	u32 pseudo_palette[16];
  45	struct mutex open_lock;
  46	unsigned int ref_count;
  47
  48	u8 crtc[VGA_CRT_C];
  49	u8 atc[VGA_ATT_C];
  50	u8 gdc[VGA_GFX_C];
  51	u8 seq[VGA_SEQ_C];
  52	u8 misc;
  53	u8 vss;
  54
  55	/* i740 specific registers */
  56	u8 display_cntl;
  57	u8 pixelpipe_cfg0;
  58	u8 pixelpipe_cfg1;
  59	u8 pixelpipe_cfg2;
  60	u8 video_clk2_m;
  61	u8 video_clk2_n;
  62	u8 video_clk2_mn_msbs;
  63	u8 video_clk2_div_sel;
  64	u8 pll_cntl;
  65	u8 address_mapping;
  66	u8 io_cntl;
  67	u8 bitblt_cntl;
  68	u8 ext_vert_total;
  69	u8 ext_vert_disp_end;
  70	u8 ext_vert_sync_start;
  71	u8 ext_vert_blank_start;
  72	u8 ext_horiz_total;
  73	u8 ext_horiz_blank;
  74	u8 ext_offset;
  75	u8 interlace_cntl;
  76	u32 lmi_fifo_watermark;
  77	u8 ext_start_addr;
  78	u8 ext_start_addr_hi;
  79};
  80
  81#define DACSPEED8	203
  82#define DACSPEED16	163
  83#define DACSPEED24_SG	136
  84#define DACSPEED24_SD	128
  85#define DACSPEED32	86
  86
  87static const struct fb_fix_screeninfo i740fb_fix = {
  88	.id =		"i740fb",
  89	.type =		FB_TYPE_PACKED_PIXELS,
  90	.visual =	FB_VISUAL_TRUECOLOR,
  91	.xpanstep =	8,
  92	.ypanstep =	1,
  93	.accel =	FB_ACCEL_NONE,
  94};
  95
  96static inline void i740outb(struct i740fb_par *par, u16 port, u8 val)
  97{
  98	vga_mm_w(par->regs, port, val);
  99}
 100static inline u8 i740inb(struct i740fb_par *par, u16 port)
 101{
 102	return vga_mm_r(par->regs, port);
 103}
 104static inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val)
 105{
 106	vga_mm_w_fast(par->regs, port, reg, val);
 107}
 108static inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg)
 109{
 110	vga_mm_w(par->regs, port, reg);
 111	return vga_mm_r(par->regs, port+1);
 112}
 113static inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg,
 114				   u8 val, u8 mask)
 115{
 116	vga_mm_w_fast(par->regs, port, reg, (val & mask)
 117		| (i740inreg(par, port, reg) & ~mask));
 118}
 119
 120#define REG_DDC_DRIVE	0x62
 121#define REG_DDC_STATE	0x63
 122#define DDC_SCL		(1 << 3)
 123#define DDC_SDA		(1 << 2)
 124
 125static void i740fb_ddc_setscl(void *data, int val)
 126{
 127	struct i740fb_par *par = data;
 128
 129	i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL);
 130	i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL);
 131}
 132
 133static void i740fb_ddc_setsda(void *data, int val)
 134{
 135	struct i740fb_par *par = data;
 136
 137	i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA);
 138	i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA);
 139}
 140
 141static int i740fb_ddc_getscl(void *data)
 142{
 143	struct i740fb_par *par = data;
 144
 145	i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL);
 146
 147	return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL);
 148}
 149
 150static int i740fb_ddc_getsda(void *data)
 151{
 152	struct i740fb_par *par = data;
 153
 154	i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA);
 155
 156	return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA);
 157}
 158
 159static int i740fb_setup_ddc_bus(struct fb_info *info)
 160{
 161	struct i740fb_par *par = info->par;
 162
 163	strscpy(par->ddc_adapter.name, info->fix.id,
 164		sizeof(par->ddc_adapter.name));
 165	par->ddc_adapter.owner		= THIS_MODULE;
 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, freq;
 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	freq = (((u32)1e9) / var->pixclock) * (u32)(1e3);
 647	if (freq < I740_RFREQ_FIX) {
 648		fb_dbg(info, "invalid pixclock\n");
 649		freq = I740_RFREQ_FIX;
 650	}
 651	i740_calc_vclk(freq, par);
 652
 653	/* Since we program the clocks ourselves, always use VCLK2. */
 654	par->misc |= 0x0C;
 655
 656	/* Calculate the FIFO Watermark and Burst Length. */
 657	par->lmi_fifo_watermark =
 658		i740_calc_fifo(par, 1000000 / var->pixclock, bpp);
 659
 660	return 0;
 661}
 662
 663static int i740fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 664{
 665	if (!var->pixclock)
 666		return -EINVAL;
 667
 668	switch (var->bits_per_pixel) {
 669	case 8:
 670		var->red.offset	= var->green.offset = var->blue.offset = 0;
 671		var->red.length	= var->green.length = var->blue.length = 8;
 672		break;
 673	case 16:
 674		switch (var->green.length) {
 675		default:
 676		case 5:
 677			var->red.offset = 10;
 678			var->green.offset = 5;
 679			var->blue.offset = 0;
 680			var->red.length	= 5;
 681			var->green.length = 5;
 682			var->blue.length = 5;
 683			break;
 684		case 6:
 685			var->red.offset = 11;
 686			var->green.offset = 5;
 687			var->blue.offset = 0;
 688			var->red.length = var->blue.length = 5;
 689			break;
 690		}
 691		break;
 692	case 24:
 693		var->red.offset = 16;
 694		var->green.offset = 8;
 695		var->blue.offset = 0;
 696		var->red.length	= var->green.length = var->blue.length = 8;
 697		break;
 698	case 32:
 699		var->transp.offset = 24;
 700		var->red.offset = 16;
 701		var->green.offset = 8;
 702		var->blue.offset = 0;
 703		var->transp.length = 8;
 704		var->red.length = var->green.length = var->blue.length = 8;
 705		break;
 706	default:
 707		return -EINVAL;
 708	}
 709
 710	if (var->xres > var->xres_virtual)
 711		var->xres_virtual = var->xres;
 712
 713	if (var->yres > var->yres_virtual)
 714		var->yres_virtual = var->yres;
 715
 716	if (info->monspecs.hfmax && info->monspecs.vfmax &&
 717	    info->monspecs.dclkmax && fb_validate_mode(var, info) < 0)
 718		return -EINVAL;
 719
 720	return 0;
 721}
 722
 723static void vga_protect(struct i740fb_par *par)
 724{
 725	/* disable the display */
 726	i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0x20, 0x20);
 727
 728	i740inb(par, 0x3DA);
 729	i740outb(par, VGA_ATT_W, 0x00);	/* enable palette access */
 730}
 731
 732static void vga_unprotect(struct i740fb_par *par)
 733{
 734	/* reenable display */
 735	i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0, 0x20);
 736
 737	i740inb(par, 0x3DA);
 738	i740outb(par, VGA_ATT_W, 0x20);	/* disable palette access */
 739}
 740
 741static int i740fb_set_par(struct fb_info *info)
 742{
 743	struct i740fb_par *par = info->par;
 744	u32 itemp;
 745	int i;
 746
 747	i = i740fb_decode_var(&info->var, par, info);
 748	if (i)
 749		return i;
 750
 751	memset_io(info->screen_base, 0, info->screen_size);
 752
 753	vga_protect(par);
 754
 755	i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_DISABLE);
 756
 757	mdelay(1);
 758
 759	i740outreg(par, XRX, VCLK2_VCO_M, par->video_clk2_m);
 760	i740outreg(par, XRX, VCLK2_VCO_N, par->video_clk2_n);
 761	i740outreg(par, XRX, VCLK2_VCO_MN_MSBS, par->video_clk2_mn_msbs);
 762	i740outreg(par, XRX, VCLK2_VCO_DIV_SEL, par->video_clk2_div_sel);
 763
 764	i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0,
 765			par->pixelpipe_cfg0 & DAC_8_BIT, 0x80);
 766
 767	i740inb(par, 0x3DA);
 768	i740outb(par, 0x3C0, 0x00);
 769
 770	/* update misc output register */
 771	i740outb(par, VGA_MIS_W, par->misc | 0x01);
 772
 773	/* synchronous reset on */
 774	i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x01);
 775	/* write sequencer registers */
 776	i740outreg(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE,
 777			par->seq[VGA_SEQ_CLOCK_MODE] | 0x20);
 778	for (i = 2; i < VGA_SEQ_C; i++)
 779		i740outreg(par, VGA_SEQ_I, i, par->seq[i]);
 780
 781	/* synchronous reset off */
 782	i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x03);
 783
 784	/* deprotect CRT registers 0-7 */
 785	i740outreg(par, VGA_CRT_IC, VGA_CRTC_V_SYNC_END,
 786			par->crtc[VGA_CRTC_V_SYNC_END]);
 787
 788	/* write CRT registers */
 789	for (i = 0; i < VGA_CRT_C; i++)
 790		i740outreg(par, VGA_CRT_IC, i, par->crtc[i]);
 791
 792	/* write graphics controller registers */
 793	for (i = 0; i < VGA_GFX_C; i++)
 794		i740outreg(par, VGA_GFX_I, i, par->gdc[i]);
 795
 796	/* write attribute controller registers */
 797	for (i = 0; i < VGA_ATT_C; i++) {
 798		i740inb(par, VGA_IS1_RC);		/* reset flip-flop */
 799		i740outb(par, VGA_ATT_IW, i);
 800		i740outb(par, VGA_ATT_IW, par->atc[i]);
 801	}
 802
 803	i740inb(par, VGA_IS1_RC);
 804	i740outb(par, VGA_ATT_IW, 0x20);
 805
 806	i740outreg(par, VGA_CRT_IC, EXT_VERT_TOTAL, par->ext_vert_total);
 807	i740outreg(par, VGA_CRT_IC, EXT_VERT_DISPLAY, par->ext_vert_disp_end);
 808	i740outreg(par, VGA_CRT_IC, EXT_VERT_SYNC_START,
 809			par->ext_vert_sync_start);
 810	i740outreg(par, VGA_CRT_IC, EXT_VERT_BLANK_START,
 811			par->ext_vert_blank_start);
 812	i740outreg(par, VGA_CRT_IC, EXT_HORIZ_TOTAL, par->ext_horiz_total);
 813	i740outreg(par, VGA_CRT_IC, EXT_HORIZ_BLANK, par->ext_horiz_blank);
 814	i740outreg(par, VGA_CRT_IC, EXT_OFFSET, par->ext_offset);
 815	i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, par->ext_start_addr_hi);
 816	i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, par->ext_start_addr);
 817
 818	i740outreg_mask(par, VGA_CRT_IC, INTERLACE_CNTL,
 819			par->interlace_cntl, INTERLACE_ENABLE);
 820	i740outreg_mask(par, XRX, ADDRESS_MAPPING, par->address_mapping, 0x1F);
 821	i740outreg_mask(par, XRX, BITBLT_CNTL, par->bitblt_cntl, COLEXP_MODE);
 822	i740outreg_mask(par, XRX, DISPLAY_CNTL,
 823			par->display_cntl, VGA_WRAP_MODE | GUI_MODE);
 824	i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, par->pixelpipe_cfg0, 0x9B);
 825	i740outreg_mask(par, XRX, PIXPIPE_CONFIG_2, par->pixelpipe_cfg2, 0x0C);
 826
 827	i740outreg(par, XRX, PLL_CNTL, par->pll_cntl);
 828
 829	i740outreg_mask(par, XRX, PIXPIPE_CONFIG_1,
 830			par->pixelpipe_cfg1, DISPLAY_COLOR_MODE);
 831
 832	itemp = readl(par->regs + FWATER_BLC);
 833	itemp &= ~(LMI_BURST_LENGTH | LMI_FIFO_WATERMARK);
 834	itemp |= par->lmi_fifo_watermark;
 835	writel(itemp, par->regs + FWATER_BLC);
 836
 837	i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_60HZ);
 838
 839	i740outreg_mask(par, MRX, COL_KEY_CNTL_1, 0, BLANK_DISP_OVERLAY);
 840	i740outreg_mask(par, XRX, IO_CTNL,
 841			par->io_cntl, EXTENDED_ATTR_CNTL | EXTENDED_CRTC_CNTL);
 842
 843	if (par->pixelpipe_cfg1 != DISPLAY_8BPP_MODE) {
 844		i740outb(par, VGA_PEL_MSK, 0xFF);
 845		i740outb(par, VGA_PEL_IW, 0x00);
 846		for (i = 0; i < 256; i++) {
 847			itemp = (par->pixelpipe_cfg0 & DAC_8_BIT) ? i : i >> 2;
 848			i740outb(par, VGA_PEL_D, itemp);
 849			i740outb(par, VGA_PEL_D, itemp);
 850			i740outb(par, VGA_PEL_D, itemp);
 851		}
 852	}
 853
 854	/* Wait for screen to stabilize. */
 855	mdelay(50);
 856	vga_unprotect(par);
 857
 858	info->fix.line_length =
 859			info->var.xres_virtual * info->var.bits_per_pixel / 8;
 860	if (info->var.bits_per_pixel == 8)
 861		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 862	else
 863		info->fix.visual = FB_VISUAL_TRUECOLOR;
 864
 865	return 0;
 866}
 867
 868static int i740fb_setcolreg(unsigned regno, unsigned red, unsigned green,
 869			   unsigned blue, unsigned transp,
 870			   struct fb_info *info)
 871{
 872	u32 r, g, b;
 873
 874	dev_dbg(info->device, "setcolreg: regno: %i, red=%d, green=%d, blue=%d, transp=%d, bpp=%d\n",
 875		regno, red, green, blue, transp, info->var.bits_per_pixel);
 876
 877	switch (info->fix.visual) {
 878	case FB_VISUAL_PSEUDOCOLOR:
 879		if (regno >= 256)
 880			return -EINVAL;
 881		i740outb(info->par, VGA_PEL_IW, regno);
 882		i740outb(info->par, VGA_PEL_D, red >> 8);
 883		i740outb(info->par, VGA_PEL_D, green >> 8);
 884		i740outb(info->par, VGA_PEL_D, blue >> 8);
 885		break;
 886	case FB_VISUAL_TRUECOLOR:
 887		if (regno >= 16)
 888			return -EINVAL;
 889		r = (red >> (16 - info->var.red.length))
 890			<< info->var.red.offset;
 891		b = (blue >> (16 - info->var.blue.length))
 892			<< info->var.blue.offset;
 893		g = (green >> (16 - info->var.green.length))
 894			<< info->var.green.offset;
 895		((u32 *) info->pseudo_palette)[regno] = r | g | b;
 896		break;
 897	default:
 898		return -EINVAL;
 899	}
 900
 901	return 0;
 902}
 903
 904static int i740fb_pan_display(struct fb_var_screeninfo *var,
 905				 struct fb_info *info)
 906{
 907	struct i740fb_par *par = info->par;
 908	u32 base = (var->yoffset * info->var.xres_virtual
 909		 + (var->xoffset & ~7)) >> 2;
 910
 911	dev_dbg(info->device, "pan_display: xoffset: %i yoffset: %i base: %i\n",
 912		var->xoffset, var->yoffset, base);
 913
 914	switch (info->var.bits_per_pixel) {
 915	case 8:
 916		break;
 917	case 15:
 918	case 16:
 919		base *= 2;
 920		break;
 921	case 24:
 922		/*
 923		 * The last bit does not seem to have any effect on the start
 924		 * address register in 24bpp mode, so...
 925		 */
 926		base &= 0xFFFFFFFE; /* ...ignore the last bit. */
 927		base *= 3;
 928		break;
 929	case 32:
 930		base *= 4;
 931		break;
 932	}
 933
 934	par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF;
 935	par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >>  8;
 936	par->ext_start_addr_hi = (base & 0x3FC00000) >> 22;
 937	par->ext_start_addr =
 938			((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE;
 939
 940	i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_LO,  base & 0x000000FF);
 941	i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_HI,
 942			(base & 0x0000FF00) >> 8);
 943	i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI,
 944			(base & 0x3FC00000) >> 22);
 945	i740outreg(par, VGA_CRT_IC, EXT_START_ADDR,
 946			((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE);
 947
 948	return 0;
 949}
 950
 951static int i740fb_blank(int blank_mode, struct fb_info *info)
 952{
 953	struct i740fb_par *par = info->par;
 954
 955	unsigned char SEQ01;
 956	int DPMSSyncSelect;
 957
 958	switch (blank_mode) {
 959	case FB_BLANK_UNBLANK:
 960	case FB_BLANK_NORMAL:
 961		SEQ01 = 0x00;
 962		DPMSSyncSelect = HSYNC_ON | VSYNC_ON;
 963		break;
 964	case FB_BLANK_VSYNC_SUSPEND:
 965		SEQ01 = 0x20;
 966		DPMSSyncSelect = HSYNC_ON | VSYNC_OFF;
 967		break;
 968	case FB_BLANK_HSYNC_SUSPEND:
 969		SEQ01 = 0x20;
 970		DPMSSyncSelect = HSYNC_OFF | VSYNC_ON;
 971		break;
 972	case FB_BLANK_POWERDOWN:
 973		SEQ01 = 0x20;
 974		DPMSSyncSelect = HSYNC_OFF | VSYNC_OFF;
 975		break;
 976	default:
 977		return -EINVAL;
 978	}
 979	/* Turn the screen on/off */
 980	i740outb(par, SRX, 0x01);
 981	SEQ01 |= i740inb(par, SRX + 1) & ~0x20;
 982	i740outb(par, SRX, 0x01);
 983	i740outb(par, SRX + 1, SEQ01);
 984
 985	/* Set the DPMS mode */
 986	i740outreg(par, XRX, DPMS_SYNC_SELECT, DPMSSyncSelect);
 987
 988	/* Let fbcon do a soft blank for us */
 989	return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
 990}
 991
 992static const struct fb_ops i740fb_ops = {
 993	.owner		= THIS_MODULE,
 994	.fb_open	= i740fb_open,
 995	.fb_release	= i740fb_release,
 996	FB_DEFAULT_IOMEM_OPS,
 997	.fb_check_var	= i740fb_check_var,
 998	.fb_set_par	= i740fb_set_par,
 999	.fb_setcolreg	= i740fb_setcolreg,
1000	.fb_blank	= i740fb_blank,
1001	.fb_pan_display	= i740fb_pan_display,
1002};
1003
1004/* ------------------------------------------------------------------------- */
1005
1006static int i740fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
1007{
1008	struct fb_info *info;
1009	struct i740fb_par *par;
1010	int ret, tmp;
1011	bool found = false;
1012	u8 *edid;
1013
1014	ret = aperture_remove_conflicting_pci_devices(dev, "i740fb");
1015	if (ret)
1016		return ret;
1017
1018	info = framebuffer_alloc(sizeof(struct i740fb_par), &(dev->dev));
1019	if (!info)
1020		return -ENOMEM;
1021
1022	par = info->par;
1023	mutex_init(&par->open_lock);
1024
1025	info->var.activate = FB_ACTIVATE_NOW;
1026	info->var.bits_per_pixel = 8;
1027	info->fbops = &i740fb_ops;
1028	info->pseudo_palette = par->pseudo_palette;
1029
1030	ret = pci_enable_device(dev);
1031	if (ret) {
1032		dev_err(info->device, "cannot enable PCI device\n");
1033		goto err_enable_device;
1034	}
1035
1036	ret = pci_request_regions(dev, info->fix.id);
1037	if (ret) {
1038		dev_err(info->device, "error requesting regions\n");
1039		goto err_request_regions;
1040	}
1041
1042	info->screen_base = pci_ioremap_wc_bar(dev, 0);
1043	if (!info->screen_base) {
1044		dev_err(info->device, "error remapping base\n");
1045		ret = -ENOMEM;
1046		goto err_ioremap_1;
1047	}
1048
1049	par->regs = pci_ioremap_bar(dev, 1);
1050	if (!par->regs) {
1051		dev_err(info->device, "error remapping MMIO\n");
1052		ret = -ENOMEM;
1053		goto err_ioremap_2;
1054	}
1055
1056	/* detect memory size */
1057	if ((i740inreg(par, XRX, DRAM_ROW_TYPE) & DRAM_ROW_1)
1058							== DRAM_ROW_1_SDRAM)
1059		i740outb(par, XRX, DRAM_ROW_BNDRY_1);
1060	else
1061		i740outb(par, XRX, DRAM_ROW_BNDRY_0);
1062	info->screen_size = i740inb(par, XRX + 1) * 1024 * 1024;
1063	/* detect memory type */
1064	tmp = i740inreg(par, XRX, DRAM_ROW_CNTL_LO);
1065	par->has_sgram = !((tmp & DRAM_RAS_TIMING) ||
1066			   (tmp & DRAM_RAS_PRECHARGE));
1067
1068	fb_info(info, "Intel740 on %s, %ld KB %s\n",
1069		pci_name(dev), info->screen_size >> 10,
1070		par->has_sgram ? "SGRAM" : "SDRAM");
1071
1072	info->fix = i740fb_fix;
1073	info->fix.mmio_start = pci_resource_start(dev, 1);
1074	info->fix.mmio_len = pci_resource_len(dev, 1);
1075	info->fix.smem_start = pci_resource_start(dev, 0);
1076	info->fix.smem_len = info->screen_size;
1077	info->flags = FBINFO_HWACCEL_YPAN;
1078
1079	if (i740fb_setup_ddc_bus(info) == 0) {
1080		par->ddc_registered = true;
1081		edid = fb_ddc_read(&par->ddc_adapter);
1082		if (edid) {
1083			fb_edid_to_monspecs(edid, &info->monspecs);
1084			kfree(edid);
1085			if (!info->monspecs.modedb)
1086				dev_err(info->device,
1087					"error getting mode database\n");
1088			else {
1089				const struct fb_videomode *m;
1090
1091				fb_videomode_to_modelist(
1092					info->monspecs.modedb,
1093					info->monspecs.modedb_len,
1094					&info->modelist);
1095				m = fb_find_best_display(&info->monspecs,
1096							 &info->modelist);
1097				if (m) {
1098					fb_videomode_to_var(&info->var, m);
1099					/* fill all other info->var's fields */
1100					if (!i740fb_check_var(&info->var, info))
1101						found = true;
1102				}
1103			}
1104		}
1105	}
1106
1107	if (!mode_option && !found)
1108		mode_option = "640x480-8@60";
1109
1110	if (mode_option) {
1111		ret = fb_find_mode(&info->var, info, mode_option,
1112				   info->monspecs.modedb,
1113				   info->monspecs.modedb_len,
1114				   NULL, info->var.bits_per_pixel);
1115		if (!ret || ret == 4) {
1116			dev_err(info->device, "mode %s not found\n",
1117				mode_option);
1118			ret = -EINVAL;
1119		}
1120	}
1121
1122	fb_destroy_modedb(info->monspecs.modedb);
1123	info->monspecs.modedb = NULL;
1124
1125	/* maximize virtual vertical size for fast scrolling */
1126	info->var.yres_virtual = info->fix.smem_len * 8 /
1127			(info->var.bits_per_pixel * info->var.xres_virtual);
1128
1129	if (ret == -EINVAL)
1130		goto err_find_mode;
1131
1132	ret = fb_alloc_cmap(&info->cmap, 256, 0);
1133	if (ret) {
1134		dev_err(info->device, "cannot allocate colormap\n");
1135		goto err_alloc_cmap;
1136	}
1137
1138	ret = register_framebuffer(info);
1139	if (ret) {
1140		dev_err(info->device, "error registering framebuffer\n");
1141		goto err_reg_framebuffer;
1142	}
1143
1144	fb_info(info, "%s frame buffer device\n", info->fix.id);
1145	pci_set_drvdata(dev, info);
1146	if (mtrr)
1147		par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
1148						  info->fix.smem_len);
1149	return 0;
1150
1151err_reg_framebuffer:
1152	fb_dealloc_cmap(&info->cmap);
1153err_alloc_cmap:
1154err_find_mode:
1155	if (par->ddc_registered)
1156		i2c_del_adapter(&par->ddc_adapter);
1157	pci_iounmap(dev, par->regs);
1158err_ioremap_2:
1159	pci_iounmap(dev, info->screen_base);
1160err_ioremap_1:
1161	pci_release_regions(dev);
1162err_request_regions:
1163/*	pci_disable_device(dev); */
1164err_enable_device:
1165	framebuffer_release(info);
1166	return ret;
1167}
1168
1169static void i740fb_remove(struct pci_dev *dev)
1170{
1171	struct fb_info *info = pci_get_drvdata(dev);
1172
1173	if (info) {
1174		struct i740fb_par *par = info->par;
1175		arch_phys_wc_del(par->wc_cookie);
1176		unregister_framebuffer(info);
1177		fb_dealloc_cmap(&info->cmap);
1178		if (par->ddc_registered)
1179			i2c_del_adapter(&par->ddc_adapter);
1180		pci_iounmap(dev, par->regs);
1181		pci_iounmap(dev, info->screen_base);
1182		pci_release_regions(dev);
1183/*		pci_disable_device(dev); */
1184		framebuffer_release(info);
1185	}
1186}
1187
1188static int __maybe_unused i740fb_suspend(struct device *dev)
1189{
1190	struct fb_info *info = dev_get_drvdata(dev);
1191	struct i740fb_par *par = info->par;
1192
1193	console_lock();
1194	mutex_lock(&(par->open_lock));
1195
1196	/* do nothing if framebuffer is not active */
1197	if (par->ref_count == 0) {
1198		mutex_unlock(&(par->open_lock));
1199		console_unlock();
1200		return 0;
1201	}
1202
1203	fb_set_suspend(info, 1);
1204
1205	mutex_unlock(&(par->open_lock));
1206	console_unlock();
1207
1208	return 0;
1209}
1210
1211static int __maybe_unused i740fb_resume(struct device *dev)
1212{
1213	struct fb_info *info = dev_get_drvdata(dev);
1214	struct i740fb_par *par = info->par;
1215
1216	console_lock();
1217	mutex_lock(&(par->open_lock));
1218
1219	if (par->ref_count == 0)
1220		goto fail;
1221
1222	i740fb_set_par(info);
1223	fb_set_suspend(info, 0);
1224
1225fail:
1226	mutex_unlock(&(par->open_lock));
1227	console_unlock();
1228	return 0;
1229}
1230
1231static const struct dev_pm_ops i740fb_pm_ops = {
1232#ifdef CONFIG_PM_SLEEP
1233	.suspend	= i740fb_suspend,
1234	.resume		= i740fb_resume,
1235	.freeze		= NULL,
1236	.thaw		= i740fb_resume,
1237	.poweroff	= i740fb_suspend,
1238	.restore	= i740fb_resume,
1239#endif /* CONFIG_PM_SLEEP */
1240};
1241
1242#define I740_ID_PCI 0x00d1
1243#define I740_ID_AGP 0x7800
1244
1245static const struct pci_device_id i740fb_id_table[] = {
1246	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_PCI) },
1247	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_AGP) },
1248	{ 0 }
1249};
1250MODULE_DEVICE_TABLE(pci, i740fb_id_table);
1251
1252static struct pci_driver i740fb_driver = {
1253	.name		= "i740fb",
1254	.id_table	= i740fb_id_table,
1255	.probe		= i740fb_probe,
1256	.remove		= i740fb_remove,
1257	.driver.pm	= &i740fb_pm_ops,
1258};
1259
1260#ifndef MODULE
1261static int  __init i740fb_setup(char *options)
1262{
1263	char *opt;
1264
1265	if (!options || !*options)
1266		return 0;
1267
1268	while ((opt = strsep(&options, ",")) != NULL) {
1269		if (!*opt)
1270			continue;
1271		else if (!strncmp(opt, "mtrr:", 5))
1272			mtrr = simple_strtoul(opt + 5, NULL, 0);
1273		else
1274			mode_option = opt;
1275	}
1276
1277	return 0;
1278}
1279#endif
1280
1281static int __init i740fb_init(void)
1282{
1283#ifndef MODULE
1284	char *option = NULL;
1285#endif
1286
1287	if (fb_modesetting_disabled("i740fb"))
1288		return -ENODEV;
1289
1290#ifndef MODULE
1291	if (fb_get_options("i740fb", &option))
1292		return -ENODEV;
1293	i740fb_setup(option);
1294#endif
1295
1296	return pci_register_driver(&i740fb_driver);
1297}
1298
1299static void __exit i740fb_exit(void)
1300{
1301	pci_unregister_driver(&i740fb_driver);
1302}
1303
1304module_init(i740fb_init);
1305module_exit(i740fb_exit);
1306
1307MODULE_AUTHOR("(c) 2011 Ondrej Zary <linux@rainbow-software.org>");
1308MODULE_LICENSE("GPL");
1309MODULE_DESCRIPTION("fbdev driver for Intel740");
1310
1311module_param(mode_option, charp, 0444);
1312MODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)");
1313
1314module_param(mtrr, int, 0444);
1315MODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)");
~/ Read-only file preview.