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