xref: /openbmc/linux/drivers/staging/sm750fb/sm750.c (revision c0e297dc)
1 #include<linux/kernel.h>
2 #include<linux/module.h>
3 #include<linux/errno.h>
4 #include<linux/string.h>
5 #include<linux/mm.h>
6 #include<linux/slab.h>
7 #include<linux/delay.h>
8 #include<linux/fb.h>
9 #include<linux/ioport.h>
10 #include<linux/init.h>
11 #include<linux/pci.h>
12 #include<linux/mm_types.h>
13 #include<linux/vmalloc.h>
14 #include<linux/pagemap.h>
15 #include<linux/screen_info.h>
16 #include<linux/vmalloc.h>
17 #include<linux/pagemap.h>
18 #include <linux/console.h>
19 #include <asm/fb.h>
20 #include "sm750.h"
21 #include "sm750_hw.h"
22 #include "sm750_accel.h"
23 #include "sm750_cursor.h"
24 
25 #include "modedb.h"
26 
27 int smi_indent = 0;
28 
29 
30 /*
31  * #ifdef __BIG_ENDIAN
32  * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
33  * size_t count, loff_t *ppos);
34  * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,
35  * size_t count, loff_t *ppos);
36  * #endif
37  */
38 
39 typedef void (*PROC_SPEC_SETUP)(struct lynx_share*, char *);
40 typedef int (*PROC_SPEC_MAP)(struct lynx_share*, struct pci_dev*);
41 typedef int (*PROC_SPEC_INITHW)(struct lynx_share*, struct pci_dev*);
42 
43 
44 /* common var for all device */
45 static int g_hwcursor = 1;
46 static int g_noaccel;
47 static int g_nomtrr;
48 static const char *g_fbmode[] = {NULL, NULL};
49 static const char *g_def_fbmode = "800x600-16@60";
50 static char *g_settings = NULL;
51 static int g_dualview;
52 static char *g_option = NULL;
53 
54 
55 static const struct fb_videomode lynx750_ext[] = {
56 	/*	1024x600-60 VESA	[1.71:1] */
57 	{NULL,  60, 1024, 600, 20423, 144,  40, 18, 1, 104, 3,
58 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
59 	 FB_VMODE_NONINTERLACED},
60 
61 	/*	1024x600-70 VESA */
62 	{NULL,  70, 1024, 600, 17211, 152,  48, 21, 1, 104, 3,
63 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
64 	 FB_VMODE_NONINTERLACED},
65 
66 	/*	1024x600-75 VESA */
67 	{NULL,  75, 1024, 600, 15822, 160,  56, 23, 1, 104, 3,
68 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
69 	 FB_VMODE_NONINTERLACED},
70 
71 	/*	1024x600-85 VESA */
72 	{NULL,  85, 1024, 600, 13730, 168,  56, 26, 1, 112, 3,
73 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
74 	 FB_VMODE_NONINTERLACED},
75 
76 	/*	720x480	*/
77 	{NULL, 60,  720,  480,  37427, 88,   16, 13, 1,   72,  3,
78 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
79 	 FB_VMODE_NONINTERLACED},
80 
81 	/*	1280x720		[1.78:1]	*/
82 	{NULL, 60,  1280,  720,  13426, 162, 86, 22, 1,  136, 3,
83 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
84 	 FB_VMODE_NONINTERLACED},
85 
86 	/*	1280x768@60 */
87 	{NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7,
88 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
89 	 FB_VMODE_NONINTERLACED},
90 
91 	/*	1360 x 768	[1.77083:1]	*/
92 	{NULL,  60, 1360, 768, 11804, 208,  64, 23, 1, 144, 3,
93 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
94 	 FB_VMODE_NONINTERLACED},
95 
96 	/*	1368 x 768      [1.78:1]	*/
97 	{NULL, 60,  1368,  768,  11647, 216, 72, 23, 1,  144, 3,
98 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
99 	 FB_VMODE_NONINTERLACED},
100 
101 	/*	1440 x 900		[16:10]	*/
102 	{NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
103 	 FB_SYNC_VERT_HIGH_ACT,
104 	 FB_VMODE_NONINTERLACED},
105 
106 	/*	1440x960		[15:10]	*/
107 	{NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
108 	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
109 	 FB_VMODE_NONINTERLACED},
110 
111 	/*	1920x1080	[16:9]	*/
112 	{NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
113 	 FB_SYNC_VERT_HIGH_ACT,
114 	 FB_VMODE_NONINTERLACED},
115 };
116 
117 
118 
119 
120 /* no hardware cursor supported under version 2.6.10, kernel bug */
121 static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
122 {
123 	struct lynxfb_par *par;
124 	struct lynxfb_crtc *crtc;
125 	struct lynx_cursor *cursor;
126 
127 	par = info->par;
128 	crtc = &par->crtc;
129 	cursor = &crtc->cursor;
130 
131 	if (fbcursor->image.width > cursor->maxW ||
132 	   fbcursor->image.height > cursor->maxH ||
133 	   fbcursor->image.depth > 1) {
134 		return -ENXIO;
135 	}
136 
137 	cursor->disable(cursor);
138 	if (fbcursor->set & FB_CUR_SETSIZE)
139 		cursor->setSize(cursor,
140 				fbcursor->image.width,
141 				fbcursor->image.height);
142 
143 	if (fbcursor->set & FB_CUR_SETPOS)
144 		cursor->setPos(cursor,
145 			       fbcursor->image.dx - info->var.xoffset,
146 			       fbcursor->image.dy - info->var.yoffset);
147 
148 	if (fbcursor->set & FB_CUR_SETCMAP) {
149 		/* get the 16bit color of kernel means */
150 		u16 fg, bg;
151 
152 		fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))|
153 		      ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)|
154 		      ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
155 
156 		bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))|
157 		      ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)|
158 		      ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
159 
160 		cursor->setColor(cursor, fg, bg);
161 	}
162 
163 
164 	if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
165 		cursor->setData(cursor,
166 				fbcursor->rop,
167 				fbcursor->image.data,
168 				fbcursor->mask);
169 	}
170 
171 	if (fbcursor->enable)
172 		cursor->enable(cursor);
173 
174 	return 0;
175 }
176 
177 static void lynxfb_ops_fillrect(struct fb_info *info,
178 				const struct fb_fillrect *region)
179 {
180 	struct lynxfb_par *par;
181 	struct lynx_share *share;
182 	unsigned int base, pitch, Bpp, rop;
183 	u32 color;
184 
185 	if (info->state != FBINFO_STATE_RUNNING)
186 		return;
187 
188 	par = info->par;
189 	share = par->share;
190 
191 	/* each time 2d function begin to work,below three variable always need
192 	 * be set, seems we can put them together in some place  */
193 	base = par->crtc.oScreen;
194 	pitch = info->fix.line_length;
195 	Bpp = info->var.bits_per_pixel >> 3;
196 
197 	color = (Bpp == 1)?region->color:((u32 *)info->pseudo_palette)[region->color];
198 	rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR:HW_ROP2_COPY;
199 
200 	/*
201 	 * If not use spin_lock,system will die if user load driver
202 	 * and immediately unload driver frequently (dual)
203 	 */
204 	if (share->dual)
205 		spin_lock(&share->slock);
206 
207 	share->accel.de_fillrect(&share->accel,
208 				 base, pitch, Bpp,
209 				 region->dx, region->dy,
210 				 region->width, region->height,
211 				 color, rop);
212 	if (share->dual)
213 		spin_unlock(&share->slock);
214 }
215 
216 static void lynxfb_ops_copyarea(struct fb_info *info,
217 				const struct fb_copyarea *region)
218 {
219 	struct lynxfb_par *par;
220 	struct lynx_share *share;
221 	unsigned int base, pitch, Bpp;
222 
223 	par = info->par;
224 	share = par->share;
225 
226 	/* each time 2d function begin to work,below three variable always need
227 	 * be set, seems we can put them together in some place  */
228 	base = par->crtc.oScreen;
229 	pitch = info->fix.line_length;
230 	Bpp = info->var.bits_per_pixel >> 3;
231 
232 	/*
233 	 * If not use spin_lock, system will die if user load driver
234 	 * and immediately unload driver frequently (dual)
235 	 */
236 	if (share->dual)
237 		spin_lock(&share->slock);
238 
239 	share->accel.de_copyarea(&share->accel,
240 				 base, pitch, region->sx, region->sy,
241 				 base, pitch, Bpp, region->dx, region->dy,
242 				 region->width, region->height, HW_ROP2_COPY);
243 	if (share->dual)
244 		spin_unlock(&share->slock);
245 }
246 
247 static void lynxfb_ops_imageblit(struct fb_info *info,
248 				 const struct fb_image *image)
249 {
250 	unsigned int base, pitch, Bpp;
251 	unsigned int fgcol, bgcol;
252 	struct lynxfb_par *par;
253 	struct lynx_share *share;
254 
255 	par = info->par;
256 	share = par->share;
257 	/* each time 2d function begin to work,below three variable always need
258 	 * be set, seems we can put them together in some place  */
259 	base = par->crtc.oScreen;
260 	pitch = info->fix.line_length;
261 	Bpp = info->var.bits_per_pixel >> 3;
262 
263 	if (image->depth == 1) {
264 		if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
265 		    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
266 			fgcol = ((u32 *)info->pseudo_palette)[image->fg_color];
267 			bgcol = ((u32 *)info->pseudo_palette)[image->bg_color];
268 		} else {
269 			fgcol = image->fg_color;
270 			bgcol = image->bg_color;
271 		}
272 		goto _do_work;
273 	}
274 	/* TODO: Implement hardware acceleration for image->depth > 1 */
275 	cfb_imageblit(info, image);
276 	return;
277 
278 _do_work:
279 	/*
280 	 * If not use spin_lock, system will die if user load driver
281 	 * and immediately unload driver frequently (dual)
282 	 */
283 	if (share->dual)
284 		spin_lock(&share->slock);
285 
286 	share->accel.de_imageblit(&share->accel,
287 				  image->data, image->width>>3, 0,
288 				  base, pitch, Bpp,
289 				  image->dx, image->dy,
290 				  image->width, image->height,
291 				  fgcol, bgcol, HW_ROP2_COPY);
292 	if (share->dual)
293 		spin_unlock(&share->slock);
294 }
295 
296 static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
297 				  struct fb_info *info)
298 {
299 	struct lynxfb_par *par;
300 	struct lynxfb_crtc *crtc;
301 	int ret;
302 
303 
304 	if (!info)
305 		return -EINVAL;
306 
307 	ret = 0;
308 	par = info->par;
309 	crtc = &par->crtc;
310 	ret = crtc->proc_panDisplay(crtc, var, info);
311 
312 	return ret;
313 }
314 
315 static int lynxfb_ops_set_par(struct fb_info *info)
316 {
317 	struct lynxfb_par *par;
318 	struct lynx_share *share;
319 	struct lynxfb_crtc *crtc;
320 	struct lynxfb_output *output;
321 	struct fb_var_screeninfo *var;
322 	struct fb_fix_screeninfo *fix;
323 	int ret;
324 	unsigned int line_length;
325 
326 	if (!info)
327 		return -EINVAL;
328 
329 	ret = 0;
330 	par = info->par;
331 	share = par->share;
332 	crtc = &par->crtc;
333 	output = &par->output;
334 	var = &info->var;
335 	fix = &info->fix;
336 
337 	/* fix structur is not so FIX ... */
338 	line_length = var->xres_virtual * var->bits_per_pixel / 8;
339 	line_length = PADDING(crtc->line_pad, line_length);
340 	fix->line_length = line_length;
341 	pr_info("fix->line_length = %d\n", fix->line_length);
342 
343 	/* var->red,green,blue,transp are need to be set by driver
344 	 * and these data should be set before setcolreg routine
345 	 * */
346 
347 	switch (var->bits_per_pixel) {
348 	case 8:
349 		fix->visual = FB_VISUAL_PSEUDOCOLOR;
350 		var->red.offset = 0;
351 		var->red.length = 8;
352 		var->green.offset = 0;
353 		var->green.length = 8;
354 		var->blue.offset = 0;
355 		var->blue.length = 8;
356 		var->transp.length = 0;
357 		var->transp.offset = 0;
358 		break;
359 	case 16:
360 		var->red.offset = 11;
361 		var->red.length = 5;
362 		var->green.offset = 5;
363 		var->green.length = 6;
364 		var->blue.offset = 0;
365 		var->blue.length = 5;
366 		var->transp.length = 0;
367 		var->transp.offset = 0;
368 		fix->visual = FB_VISUAL_TRUECOLOR;
369 		break;
370 	case 24:
371 	case 32:
372 		var->red.offset = 16;
373 		var->red.length = 8;
374 		var->green.offset = 8;
375 		var->green.length = 8;
376 		var->blue.offset = 0;
377 		var->blue.length = 8;
378 		fix->visual = FB_VISUAL_TRUECOLOR;
379 		break;
380 	default:
381 		ret = -EINVAL;
382 		break;
383 	}
384 	var->height = var->width = -1;
385 	var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
386 
387 	if (ret) {
388 		pr_err("pixel bpp format not satisfied\n.");
389 		return ret;
390 	}
391 	ret = crtc->proc_setMode(crtc, var, fix);
392 	if (!ret)
393 		ret = output->proc_setMode(output, var, fix);
394 	return ret;
395 }
396 
397 static inline unsigned int chan_to_field(unsigned int chan,
398 					 struct fb_bitfield *bf)
399 {
400 	chan &= 0xffff;
401 	chan >>= 16 - bf->length;
402 	return chan << bf->offset;
403 }
404 
405 #ifdef CONFIG_PM
406 static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg)
407 {
408 	struct fb_info *info;
409 	struct lynx_share *share;
410 	int ret;
411 
412 	if (mesg.event == pdev->dev.power.power_state.event)
413 		return 0;
414 
415 	ret = 0;
416 	share = pci_get_drvdata(pdev);
417 	switch (mesg.event) {
418 	case PM_EVENT_FREEZE:
419 	case PM_EVENT_PRETHAW:
420 		pdev->dev.power.power_state = mesg;
421 		return 0;
422 	}
423 
424 	console_lock();
425 	if (mesg.event & PM_EVENT_SLEEP) {
426 		info = share->fbinfo[0];
427 		if (info)
428 			/* 1 means do suspend */
429 			fb_set_suspend(info, 1);
430 		info = share->fbinfo[1];
431 		if (info)
432 			/* 1 means do suspend */
433 			fb_set_suspend(info, 1);
434 
435 		ret = pci_save_state(pdev);
436 		if (ret) {
437 			pr_err("error:%d occurred in pci_save_state\n", ret);
438 			return ret;
439 		}
440 
441 		/* set chip to sleep mode */
442 		if (share->suspend)
443 			(*share->suspend)(share);
444 
445 		pci_disable_device(pdev);
446 		ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
447 		if (ret) {
448 			pr_err("error:%d occurred in pci_set_power_state\n", ret);
449 			return ret;
450 		}
451 	}
452 
453 	pdev->dev.power.power_state = mesg;
454 	console_unlock();
455 	return ret;
456 }
457 
458 static int lynxfb_resume(struct pci_dev *pdev)
459 {
460 	struct fb_info *info;
461 	struct lynx_share *share;
462 
463 	struct lynxfb_par *par;
464 	struct lynxfb_crtc *crtc;
465 	struct lynx_cursor *cursor;
466 
467 	int ret;
468 
469 
470 	ret = 0;
471 	share = pci_get_drvdata(pdev);
472 
473 	console_lock();
474 
475 	ret = pci_set_power_state(pdev, PCI_D0);
476 	if (ret) {
477 		pr_err("error:%d occurred in pci_set_power_state\n", ret);
478 		return ret;
479 	}
480 
481 
482 	if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) {
483 		pci_restore_state(pdev);
484 		ret = pci_enable_device(pdev);
485 		if (ret) {
486 			pr_err("error:%d occurred in pci_enable_device\n", ret);
487 			return ret;
488 		}
489 		pci_set_master(pdev);
490 	}
491 	if (share->resume)
492 		(*share->resume)(share);
493 
494 	hw_sm750_inithw(share, pdev);
495 
496 
497 	info = share->fbinfo[0];
498 
499 	if (info) {
500 		par = info->par;
501 		crtc = &par->crtc;
502 		cursor = &crtc->cursor;
503 		memset_io(cursor->vstart, 0x0, cursor->size);
504 		memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
505 		lynxfb_ops_set_par(info);
506 		fb_set_suspend(info, 0);
507 	}
508 
509 	info = share->fbinfo[1];
510 
511 	if (info) {
512 		par = info->par;
513 		crtc = &par->crtc;
514 		cursor = &crtc->cursor;
515 		memset_io(cursor->vstart, 0x0, cursor->size);
516 		memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
517 		lynxfb_ops_set_par(info);
518 		fb_set_suspend(info, 0);
519 	}
520 
521 
522 	console_unlock();
523 	return ret;
524 }
525 #endif
526 
527 static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
528 				struct fb_info *info)
529 {
530 	struct lynxfb_par *par;
531 	struct lynxfb_crtc *crtc;
532 	struct lynxfb_output *output;
533 	struct lynx_share *share;
534 	int ret;
535 	resource_size_t request;
536 
537 
538 	par = info->par;
539 	crtc = &par->crtc;
540 	output = &par->output;
541 	share = par->share;
542 	ret = 0;
543 
544 	pr_debug("check var:%dx%d-%d\n",
545 		 var->xres,
546 		 var->yres,
547 		 var->bits_per_pixel);
548 
549 
550 	switch (var->bits_per_pixel) {
551 	case 8:
552 	case 16:
553 	case 24: /* support 24 bpp for only lynx712/722/720 */
554 	case 32:
555 		break;
556 	default:
557 		pr_err("bpp %d not supported\n", var->bits_per_pixel);
558 		ret = -EINVAL;
559 		goto exit;
560 	}
561 
562 	switch (var->bits_per_pixel) {
563 	case 8:
564 		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
565 		var->red.offset = 0;
566 		var->red.length = 8;
567 		var->green.offset = 0;
568 		var->green.length = 8;
569 		var->blue.offset = 0;
570 		var->blue.length = 8;
571 		var->transp.length = 0;
572 		var->transp.offset = 0;
573 		break;
574 	case 16:
575 		var->red.offset = 11;
576 		var->red.length = 5;
577 		var->green.offset = 5;
578 		var->green.length = 6;
579 		var->blue.offset = 0;
580 		var->blue.length = 5;
581 		var->transp.length = 0;
582 		var->transp.offset = 0;
583 		info->fix.visual = FB_VISUAL_TRUECOLOR;
584 		break;
585 	case 24:
586 	case 32:
587 		var->red.offset = 16;
588 		var->red.length = 8;
589 		var->green.offset = 8;
590 		var->green.length = 8;
591 		var->blue.offset = 0;
592 		var->blue.length = 8;
593 		info->fix.visual = FB_VISUAL_TRUECOLOR;
594 		break;
595 	default:
596 		ret = -EINVAL;
597 		break;
598 	}
599 	var->height = var->width = -1;
600 	var->accel_flags = 0;/* FB_ACCELF_TEXT; */
601 
602 	/* check if current fb's video memory big enought to hold the onscreen*/
603 	request = var->xres_virtual * (var->bits_per_pixel >> 3);
604 	/* defaulty crtc->channel go with par->index */
605 
606 	request = PADDING(crtc->line_pad, request);
607 	request = request * var->yres_virtual;
608 	if (crtc->vidmem_size < request) {
609 		pr_err("not enough video memory for mode\n");
610 		return -ENOMEM;
611 	}
612 
613 	ret = output->proc_checkMode(output, var);
614 	if (!ret)
615 		ret = crtc->proc_checkMode(crtc, var);
616 exit:
617 	return ret;
618 }
619 
620 
621 static int lynxfb_ops_setcolreg(unsigned regno,
622 				unsigned red,
623 				unsigned green,
624 				unsigned blue,
625 				unsigned transp,
626 				struct fb_info *info)
627 {
628 	struct lynxfb_par *par;
629 	struct lynxfb_crtc *crtc;
630 	struct fb_var_screeninfo *var;
631 	int ret;
632 
633 	par = info->par;
634 	crtc = &par->crtc;
635 	var = &info->var;
636 	ret = 0;
637 
638 	if (regno > 256) {
639 		pr_err("regno = %d\n", regno);
640 		return -EINVAL;
641 	}
642 
643 	if (info->var.grayscale)
644 		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
645 
646 	if (var->bits_per_pixel == 8 &&
647 	    info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
648 		red >>= 8;
649 		green >>= 8;
650 		blue >>= 8;
651 		ret = crtc->proc_setColReg(crtc, regno, red, green, blue);
652 		goto exit;
653 	}
654 
655 
656 	if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
657 		u32 val;
658 
659 		if (var->bits_per_pixel == 16 ||
660 		    var->bits_per_pixel == 32 ||
661 		    var->bits_per_pixel == 24) {
662 			val = chan_to_field(red, &var->red);
663 			val |= chan_to_field(green, &var->green);
664 			val |= chan_to_field(blue, &var->blue);
665 			par->pseudo_palette[regno] = val;
666 			goto exit;
667 		}
668 	}
669 
670 	ret = -EINVAL;
671 
672 exit:
673 	return ret;
674 }
675 
676 static int lynxfb_ops_blank(int blank, struct fb_info *info)
677 {
678 	struct lynxfb_par *par;
679 	struct lynxfb_output *output;
680 
681 	pr_debug("blank = %d.\n", blank);
682 	par = info->par;
683 	output = &par->output;
684 	return output->proc_setBLANK(output, blank);
685 }
686 
687 static int sm750fb_set_drv(struct lynxfb_par *par)
688 {
689 	int ret;
690 	struct lynx_share *share;
691 	struct sm750_share *spec_share;
692 	struct lynxfb_output *output;
693 	struct lynxfb_crtc *crtc;
694 
695 	ret = 0;
696 
697 	share = par->share;
698 	spec_share = container_of(share, struct sm750_share, share);
699 	output = &par->output;
700 	crtc = &par->crtc;
701 
702 	crtc->vidmem_size = (share->dual)?share->vidmem_size>>1:share->vidmem_size;
703 	/* setup crtc and output member */
704 	spec_share->hwCursor = g_hwcursor;
705 
706 	crtc->proc_setMode = hw_sm750_crtc_setMode;
707 	crtc->proc_checkMode = hw_sm750_crtc_checkMode;
708 	crtc->proc_setColReg = hw_sm750_setColReg;
709 	crtc->proc_panDisplay = hw_sm750_pan_display;
710 	crtc->clear = hw_sm750_crtc_clear;
711 	crtc->line_pad = 16;
712 	crtc->xpanstep = 8;
713 	crtc->ypanstep = 1;
714 	crtc->ywrapstep = 0;
715 
716 	output->proc_setMode = hw_sm750_output_setMode;
717 	output->proc_checkMode = hw_sm750_output_checkMode;
718 
719 	output->proc_setBLANK = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_setBLANK:hw_sm750_setBLANK;
720 	output->clear = hw_sm750_output_clear;
721 	/* chip specific phase */
722 	share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait : hw_sm750_deWait;
723 	switch (spec_share->state.dataflow) {
724 	case sm750_simul_pri:
725 		output->paths = sm750_pnc;
726 		crtc->channel = sm750_primary;
727 		crtc->oScreen = 0;
728 		crtc->vScreen = share->pvMem;
729 		pr_info("use simul primary mode\n");
730 		break;
731 	case sm750_simul_sec:
732 		output->paths = sm750_pnc;
733 		crtc->channel = sm750_secondary;
734 		crtc->oScreen = 0;
735 		crtc->vScreen = share->pvMem;
736 		break;
737 	case sm750_dual_normal:
738 		if (par->index == 0) {
739 			output->paths = sm750_panel;
740 			crtc->channel = sm750_primary;
741 			crtc->oScreen = 0;
742 			crtc->vScreen = share->pvMem;
743 		} else {
744 			output->paths = sm750_crt;
745 			crtc->channel = sm750_secondary;
746 			/* not consider of padding stuffs for oScreen,need fix */
747 			crtc->oScreen = (share->vidmem_size >> 1);
748 			crtc->vScreen = share->pvMem + crtc->oScreen;
749 		}
750 		break;
751 	case sm750_dual_swap:
752 		if (par->index == 0) {
753 			output->paths = sm750_panel;
754 			crtc->channel = sm750_secondary;
755 			crtc->oScreen = 0;
756 			crtc->vScreen = share->pvMem;
757 		} else {
758 			output->paths = sm750_crt;
759 			crtc->channel = sm750_primary;
760 			/* not consider of padding stuffs for oScreen,need fix */
761 			crtc->oScreen = (share->vidmem_size >> 1);
762 			crtc->vScreen = share->pvMem + crtc->oScreen;
763 		}
764 		break;
765 	default:
766 		ret = -EINVAL;
767 	}
768 
769 	return ret;
770 }
771 
772 static struct fb_ops lynxfb_ops = {
773 	.owner = THIS_MODULE,
774 	.fb_check_var =  lynxfb_ops_check_var,
775 	.fb_set_par = lynxfb_ops_set_par,
776 	.fb_setcolreg = lynxfb_ops_setcolreg,
777 	.fb_blank = lynxfb_ops_blank,
778 	.fb_fillrect = cfb_fillrect,
779 	.fb_imageblit = cfb_imageblit,
780 	.fb_copyarea = cfb_copyarea,
781 	/* cursor */
782 	.fb_cursor = lynxfb_ops_cursor,
783 };
784 
785 
786 static int lynxfb_set_fbinfo(struct fb_info *info, int index)
787 {
788 	int i;
789 	struct lynxfb_par *par;
790 	struct lynx_share *share;
791 	struct lynxfb_crtc *crtc;
792 	struct lynxfb_output *output;
793 	struct fb_var_screeninfo *var;
794 	struct fb_fix_screeninfo *fix;
795 
796 	const struct fb_videomode *pdb[] = {
797 		lynx750_ext, NULL, vesa_modes,
798 	};
799 	int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
800 	static const char *mdb_desc[] = {
801 		"driver prepared modes",
802 		"kernel prepared default modedb",
803 		"kernel HELPERS prepared vesa_modes",
804 	};
805 
806 
807 	static const char *fixId[2] = {
808 		"sm750_fb1", "sm750_fb2",
809 	};
810 
811 	int ret, line_length;
812 
813 	ret = 0;
814 	par = (struct lynxfb_par *)info->par;
815 	share = par->share;
816 	crtc = &par->crtc;
817 	output = &par->output;
818 	var = &info->var;
819 	fix = &info->fix;
820 
821 	/* set index */
822 	par->index = index;
823 	output->channel = &crtc->channel;
824 	sm750fb_set_drv(par);
825 	lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
826 
827 
828 	/* set current cursor variable and proc pointer,
829 	 * must be set after crtc member initialized */
830 	crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
831 	crtc->cursor.mmio = share->pvReg + 0x800f0 + (int)crtc->channel * 0x140;
832 
833 	pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
834 	crtc->cursor.maxH = crtc->cursor.maxW = 64;
835 	crtc->cursor.size = crtc->cursor.maxH*crtc->cursor.maxW*2/8;
836 	crtc->cursor.disable = hw_cursor_disable;
837 	crtc->cursor.enable = hw_cursor_enable;
838 	crtc->cursor.setColor = hw_cursor_setColor;
839 	crtc->cursor.setPos = hw_cursor_setPos;
840 	crtc->cursor.setSize = hw_cursor_setSize;
841 	crtc->cursor.setData = hw_cursor_setData;
842 	crtc->cursor.vstart = share->pvMem + crtc->cursor.offset;
843 
844 
845 	crtc->cursor.share = share;
846 		memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
847 	if (!g_hwcursor) {
848 		lynxfb_ops.fb_cursor = NULL;
849 		crtc->cursor.disable(&crtc->cursor);
850 	}
851 
852 
853 	/* set info->fbops, must be set before fb_find_mode */
854 	if (!share->accel_off) {
855 		/* use 2d acceleration */
856 		lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
857 		lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
858 		lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
859 	}
860 	info->fbops = &lynxfb_ops;
861 
862 	if (!g_fbmode[index]) {
863 		g_fbmode[index] = g_def_fbmode;
864 		if (index)
865 			g_fbmode[index] = g_fbmode[0];
866 	}
867 
868 
869 	for (i = 0; i < 3; i++) {
870 
871 		ret = fb_find_mode(var, info, g_fbmode[index],
872 				   pdb[i], cdb[i], NULL, 8);
873 
874 		if (ret == 1) {
875 			pr_info("success! use specified mode:%s in %s\n",
876 				g_fbmode[index],
877 				mdb_desc[i]);
878 			break;
879 		} else if (ret == 2) {
880 			pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
881 				g_fbmode[index],
882 				mdb_desc[i]);
883 			break;
884 		} else if (ret == 3) {
885 			pr_warn("wanna use default mode\n");
886 			/*break;*/
887 		} else if (ret == 4) {
888 			pr_warn("fall back to any valid mode\n");
889 		} else {
890 			pr_warn("ret = %d,fb_find_mode failed,with %s\n",
891 				ret,
892 				mdb_desc[i]);
893 		}
894 	}
895 
896 	/* some member of info->var had been set by fb_find_mode */
897 
898 	pr_info("Member of info->var is :\n\
899 		xres=%d\n\
900 		yres=%d\n\
901 		xres_virtual=%d\n\
902 		yres_virtual=%d\n\
903 		xoffset=%d\n\
904 		yoffset=%d\n\
905 		bits_per_pixel=%d\n \
906 		...\n",
907 		var->xres,
908 		var->yres,
909 		var->xres_virtual,
910 		var->yres_virtual,
911 		var->xoffset,
912 		var->yoffset,
913 		var->bits_per_pixel);
914 
915 	/* set par */
916 	par->info = info;
917 
918 	/* set info */
919 	line_length = PADDING(crtc->line_pad,
920 			      (var->xres_virtual * var->bits_per_pixel/8));
921 
922 	info->pseudo_palette = &par->pseudo_palette[0];
923 	info->screen_base = crtc->vScreen;
924 	pr_debug("screen_base vaddr = %p\n", info->screen_base);
925 	info->screen_size = line_length * var->yres_virtual;
926 	info->flags = FBINFO_FLAG_DEFAULT|0;
927 
928 	/* set info->fix */
929 	fix->type = FB_TYPE_PACKED_PIXELS;
930 	fix->type_aux = 0;
931 	fix->xpanstep = crtc->xpanstep;
932 	fix->ypanstep = crtc->ypanstep;
933 	fix->ywrapstep = crtc->ywrapstep;
934 	fix->accel = FB_ACCEL_SMI;
935 
936 	strlcpy(fix->id, fixId[index], sizeof(fix->id));
937 
938 
939 	fix->smem_start = crtc->oScreen + share->vidmem_start;
940 	pr_info("fix->smem_start = %lx\n", fix->smem_start);
941 	/* according to mmap experiment from user space application,
942 	 * fix->mmio_len should not larger than virtual size
943 	 * (xres_virtual x yres_virtual x ByPP)
944 	 * Below line maybe buggy when user mmap fb dev node and write
945 	 * data into the bound over virtual size
946 	 * */
947 	fix->smem_len = crtc->vidmem_size;
948 	pr_info("fix->smem_len = %x\n", fix->smem_len);
949 	info->screen_size = fix->smem_len;
950 	fix->line_length = line_length;
951 	fix->mmio_start = share->vidreg_start;
952 	pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
953 	fix->mmio_len = share->vidreg_size;
954 	pr_info("fix->mmio_len = %x\n", fix->mmio_len);
955 	switch (var->bits_per_pixel) {
956 	case 8:
957 		fix->visual = FB_VISUAL_PSEUDOCOLOR;
958 		break;
959 	case 16:
960 	case 32:
961 		fix->visual = FB_VISUAL_TRUECOLOR;
962 		break;
963 	}
964 
965 	/* set var */
966 	var->activate = FB_ACTIVATE_NOW;
967 	var->accel_flags = 0;
968 	var->vmode = FB_VMODE_NONINTERLACED;
969 
970 	pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
971 		 info->cmap.start, info->cmap.len,
972 		 info->cmap.red, info->cmap.green, info->cmap.blue,
973 		 info->cmap.transp);
974 
975 	ret = fb_alloc_cmap(&info->cmap, 256, 0);
976 	if (ret < 0) {
977 		pr_err("Could not allocate memory for cmap.\n");
978 		goto exit;
979 	}
980 
981 	pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
982 		 info->cmap.start, info->cmap.len,
983 		 info->cmap.red, info->cmap.green, info->cmap.blue,
984 		 info->cmap.transp);
985 
986 exit:
987 	lynxfb_ops_check_var(var, info);
988 	return ret;
989 }
990 
991 /*	chip specific g_option configuration routine */
992 static void sm750fb_setup(struct lynx_share *share, char *src)
993 {
994 	struct sm750_share *spec_share;
995 	char *opt;
996 #ifdef CAP_EXPENSION
997 	char *exp_res;
998 #endif
999 	int swap;
1000 
1001 
1002 	spec_share = container_of(share, struct sm750_share, share);
1003 #ifdef CAP_EXPENSIION
1004 	exp_res = NULL;
1005 #endif
1006 	swap = 0;
1007 
1008 	spec_share->state.initParm.chip_clk = 0;
1009 	spec_share->state.initParm.mem_clk = 0;
1010 	spec_share->state.initParm.master_clk = 0;
1011 	spec_share->state.initParm.powerMode = 0;
1012 	spec_share->state.initParm.setAllEngOff = 0;
1013 	spec_share->state.initParm.resetMemory = 1;
1014 
1015 	/* defaultly turn g_hwcursor on for both view */
1016 	g_hwcursor = 3;
1017 
1018 	if (!src || !*src) {
1019 		pr_warn("no specific g_option.\n");
1020 		goto NO_PARAM;
1021 	}
1022 
1023 	while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
1024 		pr_info("opt=%s\n", opt);
1025 		pr_info("src=%s\n", src);
1026 
1027 		if (!strncmp(opt, "swap", strlen("swap")))
1028 			swap = 1;
1029 		else if (!strncmp(opt, "nocrt", strlen("nocrt")))
1030 			spec_share->state.nocrt = 1;
1031 		else if (!strncmp(opt, "36bit", strlen("36bit")))
1032 			spec_share->state.pnltype = sm750_doubleTFT;
1033 		else if (!strncmp(opt, "18bit", strlen("18bit")))
1034 			spec_share->state.pnltype = sm750_dualTFT;
1035 		else if (!strncmp(opt, "24bit", strlen("24bit")))
1036 			spec_share->state.pnltype = sm750_24TFT;
1037 #ifdef CAP_EXPANSION
1038 		else if (!strncmp(opt, "exp:", strlen("exp:")))
1039 			exp_res = opt + strlen("exp:");
1040 #endif
1041 		else if (!strncmp(opt, "nohwc0", strlen("nohwc0")))
1042 			g_hwcursor &= ~0x1;
1043 		else if (!strncmp(opt, "nohwc1", strlen("nohwc1")))
1044 			g_hwcursor &= ~0x2;
1045 		else if (!strncmp(opt, "nohwc", strlen("nohwc")))
1046 			g_hwcursor = 0;
1047 		else {
1048 			if (!g_fbmode[0]) {
1049 				g_fbmode[0] = opt;
1050 				pr_info("find fbmode0 : %s\n", g_fbmode[0]);
1051 			} else if (!g_fbmode[1]) {
1052 				g_fbmode[1] = opt;
1053 				pr_info("find fbmode1 : %s\n", g_fbmode[1]);
1054 			} else {
1055 				pr_warn("How many view you wann set?\n");
1056 			}
1057 		}
1058 	}
1059 #ifdef CAP_EXPANSION
1060 	if (getExpRes(exp_res,
1061 		      &spec_share->state.xLCD,
1062 		      &spec_share->state.yLCD)) {
1063 		/* seems exp_res is not valid */
1064 		spec_share->state.xLCD = spec_share->state.yLCD = 0;
1065 	}
1066 #endif
1067 
1068 NO_PARAM:
1069 	if (share->revid != SM750LE_REVISION_ID) {
1070 		if (share->dual) {
1071 			if (swap)
1072 				spec_share->state.dataflow = sm750_dual_swap;
1073 			else
1074 				spec_share->state.dataflow = sm750_dual_normal;
1075 		} else {
1076 			if (swap)
1077 				spec_share->state.dataflow = sm750_simul_sec;
1078 			else
1079 				spec_share->state.dataflow = sm750_simul_pri;
1080 		}
1081 	} else {
1082 		/* SM750LE only have one crt channel */
1083 		spec_share->state.dataflow = sm750_simul_sec;
1084 		/* sm750le do not have complex attributes */
1085 		spec_share->state.nocrt = 0;
1086 	}
1087 }
1088 
1089 static int lynxfb_pci_probe(struct pci_dev *pdev,
1090 			    const struct pci_device_id *ent)
1091 {
1092 	struct fb_info *info[] = {NULL, NULL};
1093 	struct lynx_share *share = NULL;
1094 
1095 	struct sm750_share *spec_share = NULL;
1096 	size_t spec_offset = 0;
1097 	int fbidx;
1098 
1099 
1100 	/* enable device */
1101 	if (pci_enable_device(pdev)) {
1102 		pr_err("can not enable device.\n");
1103 		goto err_enable;
1104 	}
1105 
1106 	/* though offset of share in sm750_share is 0,
1107 	 * we use this marcro as the same */
1108 	spec_offset = offsetof(struct sm750_share, share);
1109 
1110 	spec_share = kzalloc(sizeof(*spec_share), GFP_KERNEL);
1111 	if (!spec_share) {
1112 		pr_err("Could not allocate memory for share.\n");
1113 		goto err_share;
1114 	}
1115 
1116 	/* setting share structure */
1117 	share = (struct lynx_share *)(&(spec_share->share));
1118 	share->fbinfo[0] = share->fbinfo[1] = NULL;
1119 	share->devid = pdev->device;
1120 	share->revid = pdev->revision;
1121 
1122 	pr_info("share->revid = %02x\n", share->revid);
1123 	share->pdev = pdev;
1124 	share->mtrr_off = g_nomtrr;
1125 	share->mtrr.vram = 0;
1126 	share->accel_off = g_noaccel;
1127 	share->dual = g_dualview;
1128 	spin_lock_init(&share->slock);
1129 
1130 	if (!share->accel_off) {
1131 		/* hook deInit and 2d routines, notes that below hw_xxx
1132 		 * routine can work on most of lynx chips
1133 		 * if some chip need specific function,
1134 		 * please hook it in smXXX_set_drv routine */
1135 		share->accel.de_init = hw_de_init;
1136 		share->accel.de_fillrect = hw_fillrect;
1137 		share->accel.de_copyarea = hw_copyarea;
1138 		share->accel.de_imageblit = hw_imageblit;
1139 		pr_info("enable 2d acceleration\n");
1140 	} else {
1141 		pr_info("disable 2d acceleration\n");
1142 	}
1143 
1144 	/* call chip specific setup routine  */
1145 	sm750fb_setup(share, g_settings);
1146 
1147 	/* call chip specific mmap routine */
1148 	if (hw_sm750_map(share, pdev)) {
1149 		pr_err("Memory map failed\n");
1150 		goto err_map;
1151 	}
1152 
1153 	if (!share->mtrr_off)
1154 		share->mtrr.vram = arch_phys_wc_add(share->vidmem_start,
1155 						    share->vidmem_size);
1156 
1157 	memset_io(share->pvMem, 0, share->vidmem_size);
1158 
1159 	pr_info("sm%3x mmio address = %p\n", share->devid, share->pvReg);
1160 
1161 	pci_set_drvdata(pdev, share);
1162 
1163 	/* call chipInit routine */
1164 	hw_sm750_inithw(share, pdev);
1165 
1166 	/* allocate frame buffer info structor according to g_dualview */
1167 	fbidx = 0;
1168 ALLOC_FB:
1169 	info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev);
1170 	if (!info[fbidx]) {
1171 		pr_err("Could not allocate framebuffer #%d.\n", fbidx);
1172 		if (fbidx == 0)
1173 			goto err_info0_alloc;
1174 		else
1175 			goto err_info1_alloc;
1176 	} else {
1177 		struct lynxfb_par *par;
1178 		int errno;
1179 
1180 		pr_info("framebuffer #%d alloc okay\n", fbidx);
1181 		share->fbinfo[fbidx] = info[fbidx];
1182 		par = info[fbidx]->par;
1183 		par->share = share;
1184 
1185 		/* set fb_info structure */
1186 		if (lynxfb_set_fbinfo(info[fbidx], fbidx)) {
1187 			pr_err("Failed to initial fb_info #%d.\n", fbidx);
1188 			if (fbidx == 0)
1189 				goto err_info0_set;
1190 			else
1191 				goto err_info1_set;
1192 		}
1193 
1194 		/* register frame buffer */
1195 		pr_info("Ready to register framebuffer #%d.\n", fbidx);
1196 		errno = register_framebuffer(info[fbidx]);
1197 		if (errno < 0) {
1198 			pr_err("Failed to register fb_info #%d. err %d\n",
1199 			       fbidx,
1200 			       errno);
1201 			if (fbidx == 0)
1202 				goto err_register0;
1203 			else
1204 				goto err_register1;
1205 		}
1206 		pr_info("Accomplished register framebuffer #%d.\n", fbidx);
1207 	}
1208 
1209 	/* no dual view by far */
1210 	fbidx++;
1211 	if (share->dual && fbidx < 2)
1212 		goto ALLOC_FB;
1213 
1214 	return 0;
1215 
1216 err_register1:
1217 err_info1_set:
1218 	framebuffer_release(info[1]);
1219 err_info1_alloc:
1220 	unregister_framebuffer(info[0]);
1221 err_register0:
1222 err_info0_set:
1223 	framebuffer_release(info[0]);
1224 err_info0_alloc:
1225 err_map:
1226 	kfree(spec_share);
1227 err_share:
1228 err_enable:
1229 	return -ENODEV;
1230 }
1231 
1232 static void lynxfb_pci_remove(struct pci_dev *pdev)
1233 {
1234 	struct fb_info *info;
1235 	struct lynx_share *share;
1236 	void *spec_share;
1237 	struct lynxfb_par *par;
1238 	int cnt;
1239 
1240 	cnt = 2;
1241 	share = pci_get_drvdata(pdev);
1242 
1243 	while (cnt-- > 0) {
1244 		info = share->fbinfo[cnt];
1245 		if (!info)
1246 			continue;
1247 		par = info->par;
1248 
1249 		unregister_framebuffer(info);
1250 		/* clean crtc & output allocations */
1251 		par->crtc.clear(&par->crtc);
1252 		par->output.clear(&par->output);
1253 		/* release frame buffer */
1254 		framebuffer_release(info);
1255 	}
1256 	arch_phys_wc_del(share->mtrr.vram);
1257 
1258 	iounmap(share->pvReg);
1259 	iounmap(share->pvMem);
1260 	spec_share = container_of(share, struct sm750_share, share);
1261 	kfree(g_settings);
1262 	kfree(spec_share);
1263 	pci_set_drvdata(pdev, NULL);
1264 }
1265 
1266 static int __init lynxfb_setup(char *options)
1267 {
1268 	int len;
1269 	char *opt, *tmp;
1270 
1271 
1272 	if (!options || !*options) {
1273 		pr_warn("no options.\n");
1274 		return 0;
1275 	}
1276 
1277 	pr_info("options:%s\n", options);
1278 
1279 	len = strlen(options) + 1;
1280 	g_settings = kzalloc(len, GFP_KERNEL);
1281 	if (!g_settings)
1282 		return -ENOMEM;
1283 
1284 	tmp = g_settings;
1285 
1286 	/*	Notes:
1287 		char * strsep(char **s,const char * ct);
1288 		@s: the string to be searched
1289 		@ct :the characters to search for
1290 
1291 		strsep() updates @options to pointer after the first found token
1292 		it also returns the pointer ahead the token.
1293 		*/
1294 	while ((opt = strsep(&options, ":")) != NULL) {
1295 		/* options that mean for any lynx chips are configured here */
1296 		if (!strncmp(opt, "noaccel", strlen("noaccel")))
1297 			g_noaccel = 1;
1298 		else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
1299 			g_nomtrr = 1;
1300 		else if (!strncmp(opt, "dual", strlen("dual")))
1301 			g_dualview = 1;
1302 		else {
1303 			strcat(tmp, opt);
1304 			tmp += strlen(opt);
1305 			if (options != NULL)
1306 				*tmp++ = ':';
1307 			else
1308 				*tmp++ = 0;
1309 		}
1310 	}
1311 
1312 	/* misc g_settings are transport to chip specific routines */
1313 	pr_info("parameter left for chip specific analysis:%s\n", g_settings);
1314 	return 0;
1315 }
1316 
1317 static struct pci_device_id smi_pci_table[] = {
1318 	{ PCI_DEVICE(0x126f, 0x0750), },
1319 	{0,}
1320 };
1321 
1322 MODULE_DEVICE_TABLE(pci, smi_pci_table);
1323 
1324 static struct pci_driver lynxfb_driver = {
1325 	.name =		"sm750fb",
1326 	.id_table =	smi_pci_table,
1327 	.probe =	lynxfb_pci_probe,
1328 	.remove =	lynxfb_pci_remove,
1329 #ifdef CONFIG_PM
1330 	.suspend = lynxfb_suspend,
1331 	.resume = lynxfb_resume,
1332 #endif
1333 };
1334 
1335 
1336 static int __init lynxfb_init(void)
1337 {
1338 	char *option;
1339 	int ret;
1340 
1341 #ifdef MODULE
1342 	option = g_option;
1343 #else
1344 	if (fb_get_options("sm750fb", &option))
1345 		return -ENODEV;
1346 #endif
1347 
1348 	lynxfb_setup(option);
1349 	ret = pci_register_driver(&lynxfb_driver);
1350 	return ret;
1351 }
1352 module_init(lynxfb_init);
1353 
1354 static void __exit lynxfb_exit(void)
1355 {
1356 	pci_unregister_driver(&lynxfb_driver);
1357 }
1358 module_exit(lynxfb_exit);
1359 
1360 module_param(g_option, charp, S_IRUGO);
1361 
1362 MODULE_PARM_DESC(g_option,
1363 		 "\n\t\tCommon options:\n"
1364 		 "\t\tnoaccel:disable 2d capabilities\n"
1365 		 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1366 		 "\t\tdualview:dual frame buffer feature enabled\n"
1367 		 "\t\tnohwc:disable hardware cursor\n"
1368 		 "\t\tUsual example:\n"
1369 		 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1370 		 );
1371 
1372 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1373 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1374 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1375 MODULE_LICENSE("GPL v2");
1376