Blame view

drivers/video/vt8623fb.c 26.7 KB
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * linux/drivers/video/vt8623fb.c - fbdev driver for
   * integrated graphic core in VIA VT8623 [CLE266] chipset
   *
   * Copyright (c) 2006-2007 Ondrej Zajicek <santiago@crfreenet.org>
   *
   * This file is subject to the terms and conditions of the GNU General Public
   * License.  See the file COPYING in the main directory of this archive for
   * more details.
   *
   * Code is based on s3fb, some parts are from David Boucher's viafb
   * (http://davesdomain.org.uk/viafb/)
   */
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
14
15
16
17
18
19
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/string.h>
  #include <linux/mm.h>
  #include <linux/tty.h>
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
20
21
22
23
24
  #include <linux/delay.h>
  #include <linux/fb.h>
  #include <linux/svga.h>
  #include <linux/init.h>
  #include <linux/pci.h>
ac751efa6   Torben Hohn   console: rename a...
25
  #include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  #include <video/vga.h>
  
  #ifdef CONFIG_MTRR
  #include <asm/mtrr.h>
  #endif
  
  struct vt8623fb_info {
  	char __iomem *mmio_base;
  	int mtrr_reg;
  	struct vgastate state;
  	struct mutex open_lock;
  	unsigned int ref_count;
  	u32 pseudo_palette[16];
  };
  
  
  
  /* ------------------------------------------------------------------------- */
  
  static const struct svga_fb_format vt8623fb_formats[] = {
  	{ 0,  {0, 6, 0},  {0, 6, 0},  {0, 6, 0}, {0, 0, 0}, 0,
  		FB_TYPE_TEXT, FB_AUX_TEXT_SVGA_STEP8,	FB_VISUAL_PSEUDOCOLOR, 16, 16},
  	{ 4,  {0, 6, 0},  {0, 6, 0},  {0, 6, 0}, {0, 0, 0}, 0,
  		FB_TYPE_PACKED_PIXELS, 0,		FB_VISUAL_PSEUDOCOLOR, 16, 16},
  	{ 4,  {0, 6, 0},  {0, 6, 0},  {0, 6, 0}, {0, 0, 0}, 1,
  		FB_TYPE_INTERLEAVED_PLANES, 1,		FB_VISUAL_PSEUDOCOLOR, 16, 16},
  	{ 8,  {0, 6, 0},  {0, 6, 0},  {0, 6, 0}, {0, 0, 0}, 0,
  		FB_TYPE_PACKED_PIXELS, 0,		FB_VISUAL_PSEUDOCOLOR, 8, 8},
  /*	{16,  {10, 5, 0}, {5, 5, 0},  {0, 5, 0}, {0, 0, 0}, 0,
  		FB_TYPE_PACKED_PIXELS, 0,		FB_VISUAL_TRUECOLOR, 4, 4},	*/
  	{16,  {11, 5, 0}, {5, 6, 0},  {0, 5, 0}, {0, 0, 0}, 0,
  		FB_TYPE_PACKED_PIXELS, 0,		FB_VISUAL_TRUECOLOR, 4, 4},
  	{32,  {16, 8, 0}, {8, 8, 0},  {0, 8, 0}, {0, 0, 0}, 0,
  		FB_TYPE_PACKED_PIXELS, 0,		FB_VISUAL_TRUECOLOR, 2, 2},
  	SVGA_FORMAT_END
  };
  
  static const struct svga_pll vt8623_pll = {2, 127, 2, 7, 0, 3,
  	60000, 300000, 14318};
  
  /* CRT timing register sets */
3552f09a6   Adrian Bunk   vt8623fb.c: make ...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  static struct vga_regset vt8623_h_total_regs[]       = {{0x00, 0, 7}, {0x36, 3, 3}, VGA_REGSET_END};
  static struct vga_regset vt8623_h_display_regs[]     = {{0x01, 0, 7}, VGA_REGSET_END};
  static struct vga_regset vt8623_h_blank_start_regs[] = {{0x02, 0, 7}, VGA_REGSET_END};
  static struct vga_regset vt8623_h_blank_end_regs[]   = {{0x03, 0, 4}, {0x05, 7, 7}, {0x33, 5, 5}, VGA_REGSET_END};
  static struct vga_regset vt8623_h_sync_start_regs[]  = {{0x04, 0, 7}, {0x33, 4, 4}, VGA_REGSET_END};
  static struct vga_regset vt8623_h_sync_end_regs[]    = {{0x05, 0, 4}, VGA_REGSET_END};
  
  static struct vga_regset vt8623_v_total_regs[]       = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x35, 0, 0}, VGA_REGSET_END};
  static struct vga_regset vt8623_v_display_regs[]     = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x35, 2, 2}, VGA_REGSET_END};
  static struct vga_regset vt8623_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x35, 3, 3}, VGA_REGSET_END};
  static struct vga_regset vt8623_v_blank_end_regs[]   = {{0x16, 0, 7}, VGA_REGSET_END};
  static struct vga_regset vt8623_v_sync_start_regs[]  = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x35, 1, 1}, VGA_REGSET_END};
  static struct vga_regset vt8623_v_sync_end_regs[]    = {{0x11, 0, 3}, VGA_REGSET_END};
  
  static struct vga_regset vt8623_offset_regs[]        = {{0x13, 0, 7}, {0x35, 5, 7}, VGA_REGSET_END};
  static struct vga_regset vt8623_line_compare_regs[]  = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x33, 0, 2}, {0x35, 4, 4}, VGA_REGSET_END};
  static struct vga_regset vt8623_fetch_count_regs[]   = {{0x1C, 0, 7}, {0x1D, 0, 1}, VGA_REGSET_END};
  static struct vga_regset vt8623_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x34, 0, 7}, {0x48, 0, 1}, VGA_REGSET_END};
  
  static struct svga_timing_regs vt8623_timing_regs     = {
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
87
88
89
90
91
92
93
94
95
96
97
  	vt8623_h_total_regs, vt8623_h_display_regs, vt8623_h_blank_start_regs,
  	vt8623_h_blank_end_regs, vt8623_h_sync_start_regs, vt8623_h_sync_end_regs,
  	vt8623_v_total_regs, vt8623_v_display_regs, vt8623_v_blank_start_regs,
  	vt8623_v_blank_end_regs, vt8623_v_sync_start_regs, vt8623_v_sync_end_regs,
  };
  
  
  /* ------------------------------------------------------------------------- */
  
  
  /* Module parameters */
cc6c549c7   Krzysztof Helt   vt8623fb: change ...
98
  static char *mode_option = "640x480-8@60";
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
99
100
101
102
103
104
105
106
  
  #ifdef CONFIG_MTRR
  static int mtrr = 1;
  #endif
  
  MODULE_AUTHOR("(c) 2006 Ondrej Zajicek <santiago@crfreenet.org>");
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("fbdev driver for integrated graphics core in VIA VT8623 [CLE266]");
cc6c549c7   Krzysztof Helt   vt8623fb: change ...
107
108
  module_param(mode_option, charp, 0644);
  MODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)");
9e3f0ca81   Krzysztof Helt   fbdev: add remove...
109
110
  module_param_named(mode, mode_option, charp, 0);
  MODULE_PARM_DESC(mode, "Default video mode e.g. '648x480-8@60' (deprecated)");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
111
112
113
114
115
116
117
118
  
  #ifdef CONFIG_MTRR
  module_param(mtrr, int, 0444);
  MODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)");
  #endif
  
  
  /* ------------------------------------------------------------------------- */
55db09238   David Miller   svga: Make svga_t...
119
120
121
122
123
124
  static void vt8623fb_tilecursor(struct fb_info *info, struct fb_tilecursor *cursor)
  {
  	struct vt8623fb_info *par = info->par;
  
  	svga_tilecursor(par->state.vgabase, info, cursor);
  }
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
125
126
127
128
129
130
  
  static struct fb_tile_ops vt8623fb_tile_ops = {
  	.fb_settile	= svga_settile,
  	.fb_tilecopy	= svga_tilecopy,
  	.fb_tilefill    = svga_tilefill,
  	.fb_tileblit    = svga_tileblit,
55db09238   David Miller   svga: Make svga_t...
131
  	.fb_tilecursor  = vt8623fb_tilecursor,
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  	.fb_get_tilemax = svga_get_tilemax,
  };
  
  
  /* ------------------------------------------------------------------------- */
  
  
  /* image data is MSB-first, fb structure is MSB-first too */
  static inline u32 expand_color(u32 c)
  {
  	return ((c & 1) | ((c & 2) << 7) | ((c & 4) << 14) | ((c & 8) << 21)) * 0xFF;
  }
  
  /* vt8623fb_iplan_imageblit silently assumes that almost everything is 8-pixel aligned */
  static void vt8623fb_iplan_imageblit(struct fb_info *info, const struct fb_image *image)
  {
  	u32 fg = expand_color(image->fg_color);
  	u32 bg = expand_color(image->bg_color);
  	const u8 *src1, *src;
  	u8 __iomem *dst1;
  	u32 __iomem *dst;
  	u32 val;
  	int x, y;
  
  	src1 = image->data;
  	dst1 = info->screen_base + (image->dy * info->fix.line_length)
  		 + ((image->dx / 8) * 4);
  
  	for (y = 0; y < image->height; y++) {
  		src = src1;
  		dst = (u32 __iomem *) dst1;
  		for (x = 0; x < image->width; x += 8) {
  			val = *(src++) * 0x01010101;
  			val = (val & fg) | (~val & bg);
  			fb_writel(val, dst++);
  		}
  		src1 += image->width / 8;
  		dst1 += info->fix.line_length;
  	}
  }
  
  /* vt8623fb_iplan_fillrect silently assumes that almost everything is 8-pixel aligned */
  static void vt8623fb_iplan_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
  {
  	u32 fg = expand_color(rect->color);
  	u8 __iomem *dst1;
  	u32 __iomem *dst;
  	int x, y;
  
  	dst1 = info->screen_base + (rect->dy * info->fix.line_length)
  		 + ((rect->dx / 8) * 4);
  
  	for (y = 0; y < rect->height; y++) {
  		dst = (u32 __iomem *) dst1;
  		for (x = 0; x < rect->width; x += 8) {
  			fb_writel(fg, dst++);
  		}
  		dst1 += info->fix.line_length;
  	}
  }
  
  
  /* image data is MSB-first, fb structure is high-nibble-in-low-byte-first */
  static inline u32 expand_pixel(u32 c)
  {
  	return (((c &  1) << 24) | ((c &  2) << 27) | ((c &  4) << 14) | ((c &   8) << 17) |
  		((c & 16) <<  4) | ((c & 32) <<  7) | ((c & 64) >>  6) | ((c & 128) >>  3)) * 0xF;
  }
  
  /* vt8623fb_cfb4_imageblit silently assumes that almost everything is 8-pixel aligned */
  static void vt8623fb_cfb4_imageblit(struct fb_info *info, const struct fb_image *image)
  {
  	u32 fg = image->fg_color * 0x11111111;
  	u32 bg = image->bg_color * 0x11111111;
  	const u8 *src1, *src;
  	u8 __iomem *dst1;
  	u32 __iomem *dst;
  	u32 val;
  	int x, y;
  
  	src1 = image->data;
  	dst1 = info->screen_base + (image->dy * info->fix.line_length)
  		 + ((image->dx / 8) * 4);
  
  	for (y = 0; y < image->height; y++) {
  		src = src1;
  		dst = (u32 __iomem *) dst1;
  		for (x = 0; x < image->width; x += 8) {
  			val = expand_pixel(*(src++));
  			val = (val & fg) | (~val & bg);
  			fb_writel(val, dst++);
  		}
  		src1 += image->width / 8;
  		dst1 += info->fix.line_length;
  	}
  }
  
  static void vt8623fb_imageblit(struct fb_info *info, const struct fb_image *image)
  {
  	if ((info->var.bits_per_pixel == 4) && (image->depth == 1)
  	    && ((image->width % 8) == 0) && ((image->dx % 8) == 0)) {
  		if (info->fix.type == FB_TYPE_INTERLEAVED_PLANES)
  			vt8623fb_iplan_imageblit(info, image);
  		else
  			vt8623fb_cfb4_imageblit(info, image);
  	} else
  		cfb_imageblit(info, image);
  }
  
  static void vt8623fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
  {
  	if ((info->var.bits_per_pixel == 4)
  	    && ((rect->width % 8) == 0) && ((rect->dx % 8) == 0)
  	    && (info->fix.type == FB_TYPE_INTERLEAVED_PLANES))
  		vt8623fb_iplan_fillrect(info, rect);
  	 else
  		cfb_fillrect(info, rect);
  }
  
  
  /* ------------------------------------------------------------------------- */
  
  
  static void vt8623_set_pixclock(struct fb_info *info, u32 pixclock)
  {
d907ec04c   David Miller   svga: Make svga_w...
257
  	struct vt8623fb_info *par = info->par;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
258
259
260
261
262
263
264
265
266
267
268
269
  	u16 m, n, r;
  	u8 regval;
  	int rv;
  
  	rv = svga_compute_pll(&vt8623_pll, 1000000000 / pixclock, &m, &n, &r, info->node);
  	if (rv < 0) {
  		printk(KERN_ERR "fb%d: cannot set requested pixclock, keeping old value
  ", info->node);
  		return;
  	}
  
  	/* Set VGA misc register  */
ed3eb4c80   David Miller   vt8623fb: Pass pa...
270
271
  	regval = vga_r(par->state.vgabase, VGA_MIS_R);
  	vga_w(par->state.vgabase, VGA_MIS_W, regval | VGA_MIS_ENB_PLL_LOAD);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
272
273
  
  	/* Set clock registers */
ed3eb4c80   David Miller   vt8623fb: Pass pa...
274
275
  	vga_wseq(par->state.vgabase, 0x46, (n  | (r << 6)));
  	vga_wseq(par->state.vgabase, 0x47, m);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
276
277
278
279
  
  	udelay(1000);
  
  	/* PLL reset */
d907ec04c   David Miller   svga: Make svga_w...
280
281
  	svga_wseq_mask(par->state.vgabase, 0x40, 0x02, 0x02);
  	svga_wseq_mask(par->state.vgabase, 0x40, 0x00, 0x02);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
282
283
284
285
286
287
288
289
290
  }
  
  
  static int vt8623fb_open(struct fb_info *info, int user)
  {
  	struct vt8623fb_info *par = info->par;
  
  	mutex_lock(&(par->open_lock));
  	if (par->ref_count == 0) {
0144a2568   David Miller   vt8623fb: Don't c...
291
  		void __iomem *vgabase = par->state.vgabase;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
292
  		memset(&(par->state), 0, sizeof(struct vgastate));
0144a2568   David Miller   vt8623fb: Don't c...
293
  		par->state.vgabase = vgabase;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
  		par->state.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS | VGA_SAVE_CMAP;
  		par->state.num_crtc = 0xA2;
  		par->state.num_seq = 0x50;
  		save_vga(&(par->state));
  	}
  
  	par->ref_count++;
  	mutex_unlock(&(par->open_lock));
  
  	return 0;
  }
  
  static int vt8623fb_release(struct fb_info *info, int user)
  {
  	struct vt8623fb_info *par = info->par;
  
  	mutex_lock(&(par->open_lock));
  	if (par->ref_count == 0) {
  		mutex_unlock(&(par->open_lock));
  		return -EINVAL;
  	}
  
  	if (par->ref_count == 1)
  		restore_vga(&(par->state));
  
  	par->ref_count--;
  	mutex_unlock(&(par->open_lock));
  
  	return 0;
  }
  
  static int vt8623fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
  {
  	int rv, mem, step;
  
  	/* Find appropriate format */
  	rv = svga_match_format (vt8623fb_formats, var, NULL);
  	if (rv < 0)
  	{
  		printk(KERN_ERR "fb%d: unsupported mode requested
  ", info->node);
  		return rv;
  	}
  
  	/* Do not allow to have real resoulution larger than virtual */
  	if (var->xres > var->xres_virtual)
  		var->xres_virtual = var->xres;
  
  	if (var->yres > var->yres_virtual)
  		var->yres_virtual = var->yres;
  
  	/* Round up xres_virtual to have proper alignment of lines */
  	step = vt8623fb_formats[rv].xresstep - 1;
  	var->xres_virtual = (var->xres_virtual+step) & ~step;
  
  	/* Check whether have enough memory */
  	mem = ((var->bits_per_pixel * var->xres_virtual) >> 3) * var->yres_virtual;
  	if (mem > info->screen_size)
  	{
  		printk(KERN_ERR "fb%d: not enough framebuffer memory (%d kB requested , %d kB available)
  ", info->node, mem >> 10, (unsigned int) (info->screen_size >> 10));
  		return -EINVAL;
  	}
  
  	/* Text mode is limited to 256 kB of memory */
  	if ((var->bits_per_pixel == 0) && (mem > (256*1024)))
  	{
  		printk(KERN_ERR "fb%d: text framebuffer size too large (%d kB requested, 256 kB possible)
  ", info->node, mem >> 10);
  		return -EINVAL;
  	}
  
  	rv = svga_check_timings (&vt8623_timing_regs, var, info->node);
  	if (rv < 0)
  	{
  		printk(KERN_ERR "fb%d: invalid timings requested
  ", info->node);
  		return rv;
  	}
  
  	/* Interlaced mode not supported */
  	if (var->vmode & FB_VMODE_INTERLACED)
  		return -EINVAL;
  
  	return 0;
  }
  
  
  static int vt8623fb_set_par(struct fb_info *info)
  {
  	u32 mode, offset_value, fetch_value, screen_size;
21da386d0   David Miller   svga: Make svga_w...
385
  	struct vt8623fb_info *par = info->par;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  	u32 bpp = info->var.bits_per_pixel;
  
  	if (bpp != 0) {
  		info->fix.ypanstep = 1;
  		info->fix.line_length = (info->var.xres_virtual * bpp) / 8;
  
  		info->flags &= ~FBINFO_MISC_TILEBLITTING;
  		info->tileops = NULL;
  
  		/* in 4bpp supports 8p wide tiles only, any tiles otherwise */
  		info->pixmap.blit_x = (bpp == 4) ? (1 << (8 - 1)) : (~(u32)0);
  		info->pixmap.blit_y = ~(u32)0;
  
  		offset_value = (info->var.xres_virtual * bpp) / 64;
  		fetch_value  = ((info->var.xres * bpp) / 128) + 4;
  
  		if (bpp == 4)
  			fetch_value  = (info->var.xres / 8) + 8; /* + 0 is OK */
  
  		screen_size  = info->var.yres_virtual * info->fix.line_length;
  	} else {
  		info->fix.ypanstep = 16;
  		info->fix.line_length = 0;
  
  		info->flags |= FBINFO_MISC_TILEBLITTING;
  		info->tileops = &vt8623fb_tile_ops;
  
  		/* supports 8x16 tiles only */
  		info->pixmap.blit_x = 1 << (8 - 1);
  		info->pixmap.blit_y = 1 << (16 - 1);
  
  		offset_value = info->var.xres_virtual / 16;
  		fetch_value  = (info->var.xres / 8) + 8;
  		screen_size  = (info->var.xres_virtual * info->var.yres_virtual) / 64;
  	}
  
  	info->var.xoffset = 0;
  	info->var.yoffset = 0;
  	info->var.activate = FB_ACTIVATE_NOW;
  
  	/* Unlock registers */
d907ec04c   David Miller   svga: Make svga_w...
427
  	svga_wseq_mask(par->state.vgabase, 0x10, 0x01, 0x01);
ea770789d   David Miller   svga: Make svga_w...
428
429
  	svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x80);
  	svga_wcrt_mask(par->state.vgabase, 0x47, 0x00, 0x01);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
430
431
  
  	/* Device, screen and sync off */
d907ec04c   David Miller   svga: Make svga_w...
432
  	svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
ea770789d   David Miller   svga: Make svga_w...
433
434
  	svga_wcrt_mask(par->state.vgabase, 0x36, 0x30, 0x30);
  	svga_wcrt_mask(par->state.vgabase, 0x17, 0x00, 0x80);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
435
436
  
  	/* Set default values */
e2fade2c1   David Miller   svga: Make svga_s...
437
  	svga_set_default_gfx_regs(par->state.vgabase);
f51a14dde   David Miller   svga: Make svga_s...
438
  	svga_set_default_atc_regs(par->state.vgabase);
a4ade8394   David Miller   svga: Make svga_s...
439
  	svga_set_default_seq_regs(par->state.vgabase);
1d28fcadb   David Miller   svga: Make svga_s...
440
  	svga_set_default_crt_regs(par->state.vgabase);
21da386d0   David Miller   svga: Make svga_w...
441
442
  	svga_wcrt_multi(par->state.vgabase, vt8623_line_compare_regs, 0xFFFFFFFF);
  	svga_wcrt_multi(par->state.vgabase, vt8623_start_address_regs, 0);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
443

21da386d0   David Miller   svga: Make svga_w...
444
  	svga_wcrt_multi(par->state.vgabase, vt8623_offset_regs, offset_value);
dc6aff3a5   David Miller   svga: Make svga_w...
445
  	svga_wseq_multi(par->state.vgabase, vt8623_fetch_count_regs, fetch_value);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
446

8f5af9de9   Ondrej Zajicek   fbdev: vt8623fb: ...
447
  	/* Clear H/V Skew */
ea770789d   David Miller   svga: Make svga_w...
448
449
  	svga_wcrt_mask(par->state.vgabase, 0x03, 0x00, 0x60);
  	svga_wcrt_mask(par->state.vgabase, 0x05, 0x00, 0x60);
8f5af9de9   Ondrej Zajicek   fbdev: vt8623fb: ...
450

558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
451
  	if (info->var.vmode & FB_VMODE_DOUBLE)
ea770789d   David Miller   svga: Make svga_w...
452
  		svga_wcrt_mask(par->state.vgabase, 0x09, 0x80, 0x80);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
453
  	else
ea770789d   David Miller   svga: Make svga_w...
454
  		svga_wcrt_mask(par->state.vgabase, 0x09, 0x00, 0x80);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
455

d907ec04c   David Miller   svga: Make svga_w...
456
457
458
  	svga_wseq_mask(par->state.vgabase, 0x1E, 0xF0, 0xF0); // DI/DVP bus
  	svga_wseq_mask(par->state.vgabase, 0x2A, 0x0F, 0x0F); // DI/DVP bus
  	svga_wseq_mask(par->state.vgabase, 0x16, 0x08, 0xBF); // FIFO read threshold
ed3eb4c80   David Miller   vt8623fb: Pass pa...
459
460
  	vga_wseq(par->state.vgabase, 0x17, 0x1F);       // FIFO depth
  	vga_wseq(par->state.vgabase, 0x18, 0x4E);
d907ec04c   David Miller   svga: Make svga_w...
461
  	svga_wseq_mask(par->state.vgabase, 0x1A, 0x08, 0x08); // enable MMIO ?
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
462

ed3eb4c80   David Miller   vt8623fb: Pass pa...
463
464
465
466
  	vga_wcrt(par->state.vgabase, 0x32, 0x00);
  	vga_wcrt(par->state.vgabase, 0x34, 0x00);
  	vga_wcrt(par->state.vgabase, 0x6A, 0x80);
  	vga_wcrt(par->state.vgabase, 0x6A, 0xC0);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
467

ed3eb4c80   David Miller   vt8623fb: Pass pa...
468
469
470
  	vga_wgfx(par->state.vgabase, 0x20, 0x00);
  	vga_wgfx(par->state.vgabase, 0x21, 0x00);
  	vga_wgfx(par->state.vgabase, 0x22, 0x00);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
471
472
473
474
475
476
477
  
  	/* Set SR15 according to number of bits per pixel */
  	mode = svga_match_format(vt8623fb_formats, &(info->var), &(info->fix));
  	switch (mode) {
  	case 0:
  		pr_debug("fb%d: text mode
  ", info->node);
9c96394bb   David Miller   svga: Make svga_s...
478
  		svga_set_textmode_vga_regs(par->state.vgabase);
d907ec04c   David Miller   svga: Make svga_w...
479
  		svga_wseq_mask(par->state.vgabase, 0x15, 0x00, 0xFE);
ea770789d   David Miller   svga: Make svga_w...
480
  		svga_wcrt_mask(par->state.vgabase, 0x11, 0x60, 0x70);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
481
482
483
484
  		break;
  	case 1:
  		pr_debug("fb%d: 4 bit pseudocolor
  ", info->node);
ed3eb4c80   David Miller   vt8623fb: Pass pa...
485
  		vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x40);
d907ec04c   David Miller   svga: Make svga_w...
486
  		svga_wseq_mask(par->state.vgabase, 0x15, 0x20, 0xFE);
ea770789d   David Miller   svga: Make svga_w...
487
  		svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x70);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
488
489
490
491
  		break;
  	case 2:
  		pr_debug("fb%d: 4 bit pseudocolor, planar
  ", info->node);
d907ec04c   David Miller   svga: Make svga_w...
492
  		svga_wseq_mask(par->state.vgabase, 0x15, 0x00, 0xFE);
ea770789d   David Miller   svga: Make svga_w...
493
  		svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x70);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
494
495
496
497
  		break;
  	case 3:
  		pr_debug("fb%d: 8 bit pseudocolor
  ", info->node);
d907ec04c   David Miller   svga: Make svga_w...
498
  		svga_wseq_mask(par->state.vgabase, 0x15, 0x22, 0xFE);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
499
500
501
502
  		break;
  	case 4:
  		pr_debug("fb%d: 5/6/5 truecolor
  ", info->node);
d907ec04c   David Miller   svga: Make svga_w...
503
  		svga_wseq_mask(par->state.vgabase, 0x15, 0xB6, 0xFE);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
504
505
506
507
  		break;
  	case 5:
  		pr_debug("fb%d: 8/8/8 truecolor
  ", info->node);
d907ec04c   David Miller   svga: Make svga_w...
508
  		svga_wseq_mask(par->state.vgabase, 0x15, 0xAE, 0xFE);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
509
510
511
512
513
514
515
516
  		break;
  	default:
  		printk(KERN_ERR "vt8623fb: unsupported mode - bug
  ");
  		return (-EINVAL);
  	}
  
  	vt8623_set_pixclock(info, info->var.pixclock);
38d2620ea   David Miller   svga: Make svga_s...
517
  	svga_set_timings(par->state.vgabase, &vt8623_timing_regs, &(info->var), 1, 1,
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
518
519
520
521
522
523
  			 (info->var.vmode & FB_VMODE_DOUBLE) ? 2 : 1, 1,
  			 1, info->node);
  
  	memset_io(info->screen_base, 0x00, screen_size);
  
  	/* Device and screen back on */
ea770789d   David Miller   svga: Make svga_w...
524
525
  	svga_wcrt_mask(par->state.vgabase, 0x17, 0x80, 0x80);
  	svga_wcrt_mask(par->state.vgabase, 0x36, 0x00, 0x30);
d907ec04c   David Miller   svga: Make svga_w...
526
  	svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  
  	return 0;
  }
  
  
  static int vt8623fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
  				u_int transp, struct fb_info *fb)
  {
  	switch (fb->var.bits_per_pixel) {
  	case 0:
  	case 4:
  		if (regno >= 16)
  			return -EINVAL;
  
  		outb(0x0F, VGA_PEL_MSK);
  		outb(regno, VGA_PEL_IW);
  		outb(red >> 10, VGA_PEL_D);
  		outb(green >> 10, VGA_PEL_D);
  		outb(blue >> 10, VGA_PEL_D);
  		break;
  	case 8:
  		if (regno >= 256)
  			return -EINVAL;
  
  		outb(0xFF, VGA_PEL_MSK);
  		outb(regno, VGA_PEL_IW);
  		outb(red >> 10, VGA_PEL_D);
  		outb(green >> 10, VGA_PEL_D);
  		outb(blue >> 10, VGA_PEL_D);
  		break;
  	case 16:
  		if (regno >= 16)
  			return 0;
  
  		if (fb->var.green.length == 5)
  			((u32*)fb->pseudo_palette)[regno] = ((red & 0xF800) >> 1) |
  				((green & 0xF800) >> 6) | ((blue & 0xF800) >> 11);
  		else if (fb->var.green.length == 6)
  			((u32*)fb->pseudo_palette)[regno] = (red & 0xF800) |
  				((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11);
  		else
  			return -EINVAL;
  		break;
  	case 24:
  	case 32:
  		if (regno >= 16)
  			return 0;
  
  		/* ((transp & 0xFF00) << 16) */
  		((u32*)fb->pseudo_palette)[regno] = ((red & 0xFF00) << 8) |
  			(green & 0xFF00) | ((blue & 0xFF00) >> 8);
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  
  static int vt8623fb_blank(int blank_mode, struct fb_info *info)
  {
d907ec04c   David Miller   svga: Make svga_w...
589
  	struct vt8623fb_info *par = info->par;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
590
591
592
593
  	switch (blank_mode) {
  	case FB_BLANK_UNBLANK:
  		pr_debug("fb%d: unblank
  ", info->node);
ea770789d   David Miller   svga: Make svga_w...
594
  		svga_wcrt_mask(par->state.vgabase, 0x36, 0x00, 0x30);
d907ec04c   David Miller   svga: Make svga_w...
595
  		svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
596
597
598
599
  		break;
  	case FB_BLANK_NORMAL:
  		pr_debug("fb%d: blank
  ", info->node);
ea770789d   David Miller   svga: Make svga_w...
600
  		svga_wcrt_mask(par->state.vgabase, 0x36, 0x00, 0x30);
d907ec04c   David Miller   svga: Make svga_w...
601
  		svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
602
603
604
605
  		break;
  	case FB_BLANK_HSYNC_SUSPEND:
  		pr_debug("fb%d: DPMS standby (hsync off)
  ", info->node);
ea770789d   David Miller   svga: Make svga_w...
606
  		svga_wcrt_mask(par->state.vgabase, 0x36, 0x10, 0x30);
d907ec04c   David Miller   svga: Make svga_w...
607
  		svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
608
609
610
611
  		break;
  	case FB_BLANK_VSYNC_SUSPEND:
  		pr_debug("fb%d: DPMS suspend (vsync off)
  ", info->node);
ea770789d   David Miller   svga: Make svga_w...
612
  		svga_wcrt_mask(par->state.vgabase, 0x36, 0x20, 0x30);
d907ec04c   David Miller   svga: Make svga_w...
613
  		svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
614
615
616
617
  		break;
  	case FB_BLANK_POWERDOWN:
  		pr_debug("fb%d: DPMS off (no sync)
  ", info->node);
ea770789d   David Miller   svga: Make svga_w...
618
  		svga_wcrt_mask(par->state.vgabase, 0x36, 0x30, 0x30);
d907ec04c   David Miller   svga: Make svga_w...
619
  		svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
620
621
622
623
624
625
626
627
628
  		break;
  	}
  
  	return 0;
  }
  
  
  static int vt8623fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
  {
21da386d0   David Miller   svga: Make svga_w...
629
  	struct vt8623fb_info *par = info->par;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
630
631
632
  	unsigned int offset;
  
  	/* Calculate the offset */
10a6c1de2   Laurent Pinchart   vt8623fb: use dis...
633
634
635
  	if (info->var.bits_per_pixel == 0) {
  		offset = (var->yoffset / 16) * info->var.xres_virtual
  		       + var->xoffset;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
636
637
638
  		offset = offset >> 3;
  	} else {
  		offset = (var->yoffset * info->fix.line_length) +
10a6c1de2   Laurent Pinchart   vt8623fb: use dis...
639
640
  			 (var->xoffset * info->var.bits_per_pixel / 8);
  		offset = offset >> ((info->var.bits_per_pixel == 4) ? 2 : 1);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
641
642
643
  	}
  
  	/* Set the offset */
21da386d0   David Miller   svga: Make svga_w...
644
  	svga_wcrt_multi(par->state.vgabase, vt8623_start_address_regs, offset);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
  
  	return 0;
  }
  
  
  /* ------------------------------------------------------------------------- */
  
  
  /* Frame buffer operations */
  
  static struct fb_ops vt8623fb_ops = {
  	.owner		= THIS_MODULE,
  	.fb_open	= vt8623fb_open,
  	.fb_release	= vt8623fb_release,
  	.fb_check_var	= vt8623fb_check_var,
  	.fb_set_par	= vt8623fb_set_par,
  	.fb_setcolreg	= vt8623fb_setcolreg,
  	.fb_blank	= vt8623fb_blank,
  	.fb_pan_display	= vt8623fb_pan_display,
  	.fb_fillrect	= vt8623fb_fillrect,
  	.fb_copyarea	= cfb_copyarea,
  	.fb_imageblit	= vt8623fb_imageblit,
5a87ede94   Antonino A. Daplas   svgalib: move fb_...
667
  	.fb_get_caps    = svga_get_caps,
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
668
669
670
671
672
673
674
  };
  
  
  /* PCI probe */
  
  static int __devinit vt8623_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
  {
6a2f6d5e9   David Miller   vt8623fb: Compute...
675
676
  	struct pci_bus_region bus_reg;
  	struct resource vga_res;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
677
678
679
680
681
682
683
684
685
686
687
688
689
  	struct fb_info *info;
  	struct vt8623fb_info *par;
  	unsigned int memsize1, memsize2;
  	int rc;
  
  	/* Ignore secondary VGA device because there is no VGA arbitration */
  	if (! svga_primary_device(dev)) {
  		dev_info(&(dev->dev), "ignoring secondary device
  ");
  		return -ENODEV;
  	}
  
  	/* Allocate and fill driver data structure */
20e061fb7   Ondrej Zajicek   fbdev: framebuffe...
690
  	info = framebuffer_alloc(sizeof(struct vt8623fb_info), &(dev->dev));
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
  	if (! info) {
  		dev_err(&(dev->dev), "cannot allocate memory
  ");
  		return -ENOMEM;
  	}
  
  	par = info->par;
  	mutex_init(&par->open_lock);
  
  	info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
  	info->fbops = &vt8623fb_ops;
  
  	/* Prepare PCI device */
  
  	rc = pci_enable_device(dev);
  	if (rc < 0) {
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
707
708
  		dev_err(info->device, "cannot enable PCI device
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
709
710
711
712
713
  		goto err_enable_device;
  	}
  
  	rc = pci_request_regions(dev, "vt8623fb");
  	if (rc < 0) {
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
714
715
  		dev_err(info->device, "cannot reserve framebuffer region
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
716
717
718
719
720
721
722
723
724
725
726
727
  		goto err_request_regions;
  	}
  
  	info->fix.smem_start = pci_resource_start(dev, 0);
  	info->fix.smem_len = pci_resource_len(dev, 0);
  	info->fix.mmio_start = pci_resource_start(dev, 1);
  	info->fix.mmio_len = pci_resource_len(dev, 1);
  
  	/* Map physical IO memory address into kernel space */
  	info->screen_base = pci_iomap(dev, 0, 0);
  	if (! info->screen_base) {
  		rc = -ENOMEM;
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
728
729
  		dev_err(info->device, "iomap for framebuffer failed
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
730
731
732
733
734
735
  		goto err_iomap_1;
  	}
  
  	par->mmio_base = pci_iomap(dev, 1, 0);
  	if (! par->mmio_base) {
  		rc = -ENOMEM;
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
736
737
  		dev_err(info->device, "iomap for MMIO failed
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
738
739
  		goto err_iomap_2;
  	}
6a2f6d5e9   David Miller   vt8623fb: Compute...
740
741
742
743
744
745
746
747
  	bus_reg.start = 0;
  	bus_reg.end = 64 * 1024;
  
  	vga_res.flags = IORESOURCE_IO;
  
  	pcibios_bus_to_resource(dev, &vga_res, &bus_reg);
  
  	par->state.vgabase = (void __iomem *) vga_res.start;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
748
  	/* Find how many physical memory there is on card */
ed3eb4c80   David Miller   vt8623fb: Pass pa...
749
750
  	memsize1 = (vga_rseq(par->state.vgabase, 0x34) + 1) >> 1;
  	memsize2 = vga_rseq(par->state.vgabase, 0x39) << 2;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
751
752
753
754
  
  	if ((16 <= memsize1) && (memsize1 <= 64) && (memsize1 == memsize2))
  		info->screen_size = memsize1 << 20;
  	else {
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
755
756
  		dev_err(info->device, "memory size detection failed (%x %x), suppose 16 MB
  ", memsize1, memsize2);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
757
758
759
760
761
762
763
764
765
766
767
768
  		info->screen_size = 16 << 20;
  	}
  
  	info->fix.smem_len = info->screen_size;
  	strcpy(info->fix.id, "VIA VT8623");
  	info->fix.type = FB_TYPE_PACKED_PIXELS;
  	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
  	info->fix.ypanstep = 0;
  	info->fix.accel = FB_ACCEL_NONE;
  	info->pseudo_palette = (void*)par->pseudo_palette;
  
  	/* Prepare startup mode */
d6d1b650a   Rusty Russell   param: simple loc...
769
  	kparam_block_sysfs_write(mode_option);
cc6c549c7   Krzysztof Helt   vt8623fb: change ...
770
  	rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8);
d6d1b650a   Rusty Russell   param: simple loc...
771
  	kparam_unblock_sysfs_write(mode_option);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
772
773
  	if (! ((rc == 1) || (rc == 2))) {
  		rc = -EINVAL;
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
774
775
  		dev_err(info->device, "mode %s not found
  ", mode_option);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
776
777
778
779
780
  		goto err_find_mode;
  	}
  
  	rc = fb_alloc_cmap(&info->cmap, 256, 0);
  	if (rc < 0) {
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
781
782
  		dev_err(info->device, "cannot allocate colormap
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
783
784
785
786
787
  		goto err_alloc_cmap;
  	}
  
  	rc = register_framebuffer(info);
  	if (rc < 0) {
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
788
789
  		dev_err(info->device, "cannot register framebugger
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  		goto err_reg_fb;
  	}
  
  	printk(KERN_INFO "fb%d: %s on %s, %d MB RAM
  ", info->node, info->fix.id,
  		 pci_name(dev), info->fix.smem_len >> 20);
  
  	/* Record a reference to the driver data */
  	pci_set_drvdata(dev, info);
  
  #ifdef CONFIG_MTRR
  	if (mtrr) {
  		par->mtrr_reg = -1;
  		par->mtrr_reg = mtrr_add(info->fix.smem_start, info->fix.smem_len, MTRR_TYPE_WRCOMB, 1);
  	}
  #endif
  
  	return 0;
  
  	/* Error handling */
  err_reg_fb:
  	fb_dealloc_cmap(&info->cmap);
  err_alloc_cmap:
  err_find_mode:
  	pci_iounmap(dev, par->mmio_base);
  err_iomap_2:
  	pci_iounmap(dev, info->screen_base);
  err_iomap_1:
  	pci_release_regions(dev);
  err_request_regions:
  /*	pci_disable_device(dev); */
  err_enable_device:
  	framebuffer_release(info);
  	return rc;
  }
  
  /* PCI remove */
  
  static void __devexit vt8623_pci_remove(struct pci_dev *dev)
  {
  	struct fb_info *info = pci_get_drvdata(dev);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
831
832
  
  	if (info) {
38d473f99   Ondrej Zajicek   vt8623fb: arkfb: ...
833
  		struct vt8623fb_info *par = info->par;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
  #ifdef CONFIG_MTRR
  		if (par->mtrr_reg >= 0) {
  			mtrr_del(par->mtrr_reg, 0, 0);
  			par->mtrr_reg = -1;
  		}
  #endif
  
  		unregister_framebuffer(info);
  		fb_dealloc_cmap(&info->cmap);
  
  		pci_iounmap(dev, info->screen_base);
  		pci_iounmap(dev, par->mmio_base);
  		pci_release_regions(dev);
  /*		pci_disable_device(dev); */
  
  		pci_set_drvdata(dev, NULL);
  		framebuffer_release(info);
  	}
  }
  
  
  #ifdef CONFIG_PM
  /* PCI suspend */
  
  static int vt8623_pci_suspend(struct pci_dev* dev, pm_message_t state)
  {
  	struct fb_info *info = pci_get_drvdata(dev);
  	struct vt8623fb_info *par = info->par;
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
862
863
  	dev_info(info->device, "suspend
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
864

ac751efa6   Torben Hohn   console: rename a...
865
  	console_lock();
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
866
867
868
869
  	mutex_lock(&(par->open_lock));
  
  	if ((state.event == PM_EVENT_FREEZE) || (par->ref_count == 0)) {
  		mutex_unlock(&(par->open_lock));
ac751efa6   Torben Hohn   console: rename a...
870
  		console_unlock();
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
871
872
873
874
875
876
877
878
879
880
  		return 0;
  	}
  
  	fb_set_suspend(info, 1);
  
  	pci_save_state(dev);
  	pci_disable_device(dev);
  	pci_set_power_state(dev, pci_choose_state(dev, state));
  
  	mutex_unlock(&(par->open_lock));
ac751efa6   Torben Hohn   console: rename a...
881
  	console_unlock();
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
882
883
884
885
886
887
888
889
890
891
892
  
  	return 0;
  }
  
  
  /* PCI resume */
  
  static int vt8623_pci_resume(struct pci_dev* dev)
  {
  	struct fb_info *info = pci_get_drvdata(dev);
  	struct vt8623fb_info *par = info->par;
594a88197   Ondrej Zajicek   vt8623fb: fix ker...
893
894
  	dev_info(info->device, "resume
  ");
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
895

ac751efa6   Torben Hohn   console: rename a...
896
  	console_lock();
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
897
  	mutex_lock(&(par->open_lock));
950d442ad   Julia Lawall   drivers/video: re...
898
899
  	if (par->ref_count == 0)
  		goto fail;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
900
901
902
903
904
905
906
907
908
909
910
  
  	pci_set_power_state(dev, PCI_D0);
  	pci_restore_state(dev);
  
  	if (pci_enable_device(dev))
  		goto fail;
  
  	pci_set_master(dev);
  
  	vt8623fb_set_par(info);
  	fb_set_suspend(info, 0);
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
911
  fail:
950d442ad   Julia Lawall   drivers/video: re...
912
  	mutex_unlock(&(par->open_lock));
ac751efa6   Torben Hohn   console: rename a...
913
  	console_unlock();
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
  
  	return 0;
  }
  #else
  #define vt8623_pci_suspend NULL
  #define vt8623_pci_resume NULL
  #endif /* CONFIG_PM */
  
  /* List of boards that we are trying to support */
  
  static struct pci_device_id vt8623_devices[] __devinitdata = {
  	{PCI_DEVICE(PCI_VENDOR_ID_VIA, 0x3122)},
  	{0, 0, 0, 0, 0, 0, 0}
  };
  
  MODULE_DEVICE_TABLE(pci, vt8623_devices);
  
  static struct pci_driver vt8623fb_pci_driver = {
  	.name		= "vt8623fb",
  	.id_table	= vt8623_devices,
  	.probe		= vt8623_pci_probe,
  	.remove		= __devexit_p(vt8623_pci_remove),
  	.suspend	= vt8623_pci_suspend,
  	.resume		= vt8623_pci_resume,
  };
  
  /* Cleanup */
  
  static void __exit vt8623fb_cleanup(void)
  {
  	pr_debug("vt8623fb: cleaning up
  ");
  	pci_unregister_driver(&vt8623fb_pci_driver);
  }
  
  /* Driver Initialisation */
3552f09a6   Adrian Bunk   vt8623fb.c: make ...
950
  static int __init vt8623fb_init(void)
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
951
952
953
954
955
956
957
958
959
  {
  
  #ifndef MODULE
  	char *option = NULL;
  
  	if (fb_get_options("vt8623fb", &option))
  		return -ENODEV;
  
  	if (option && *option)
cc6c549c7   Krzysztof Helt   vt8623fb: change ...
960
  		mode_option = option;
558b7bd86   Ondrej Zajicek   vt8623fb: new fra...
961
962
963
964
965
966
967
968
969
970
971
972
973
  #endif
  
  	pr_debug("vt8623fb: initializing
  ");
  	return pci_register_driver(&vt8623fb_pci_driver);
  }
  
  /* ------------------------------------------------------------------------- */
  
  /* Modularization */
  
  module_init(vt8623fb_init);
  module_exit(vt8623fb_cleanup);