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