staging: sm750fb: convert pr_err to pr_info
[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
27int smi_indent = 0;
28
29
30/*
c52c3700
MC
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
81dee67e
SM
37 */
38
5ace4e10
MC
39typedef void (*PROC_SPEC_SETUP)(struct lynx_share*, char *);
40typedef int (*PROC_SPEC_MAP)(struct lynx_share*, struct pci_dev*);
41typedef int (*PROC_SPEC_INITHW)(struct lynx_share*, struct pci_dev*);
81dee67e
SM
42
43
44/* common var for all device */
45static int g_hwcursor = 1;
b30edfcd 46static int g_noaccel;
b30edfcd 47static int g_nomtrr;
27fa159b
MC
48static const char *g_fbmode[] = {NULL, NULL};
49static const char *g_def_fbmode = "800x600-16@60";
50static char *g_settings = NULL;
b30edfcd 51static int g_dualview;
27fa159b
MC
52static char *g_option = NULL;
53
81dee67e 54
81dee67e 55static const struct fb_videomode lynx750_ext[] = {
4bd9503d 56 /* 1024x600-60 VESA [1.71:1] */
81dee67e 57 {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3,
3318bb5e
MC
58 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
59 FB_VMODE_NONINTERLACED},
81dee67e 60
4bd9503d 61 /* 1024x600-70 VESA */
81dee67e 62 {NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3,
3318bb5e
MC
63 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
64 FB_VMODE_NONINTERLACED},
81dee67e 65
4bd9503d 66 /* 1024x600-75 VESA */
81dee67e 67 {NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3,
3318bb5e
MC
68 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
69 FB_VMODE_NONINTERLACED},
81dee67e 70
4bd9503d 71 /* 1024x600-85 VESA */
81dee67e 72 {NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3,
3318bb5e
MC
73 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
74 FB_VMODE_NONINTERLACED},
81dee67e
SM
75
76 /* 720x480 */
77 {NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3,
3318bb5e
MC
78 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
79 FB_VMODE_NONINTERLACED},
81dee67e
SM
80
81 /* 1280x720 [1.78:1] */
82 {NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3,
3318bb5e
MC
83 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
84 FB_VMODE_NONINTERLACED},
81dee67e 85
4bd9503d 86 /* 1280x768@60 */
45e3b3da 87 {NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7,
3318bb5e
MC
88 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
89 FB_VMODE_NONINTERLACED},
81dee67e 90
81dee67e
SM
91 /* 1360 x 768 [1.77083:1] */
92 {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
3318bb5e
MC
93 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
94 FB_VMODE_NONINTERLACED},
81dee67e
SM
95
96 /* 1368 x 768 [1.78:1] */
97 {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
3318bb5e
MC
98 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
99 FB_VMODE_NONINTERLACED},
81dee67e 100
4bd9503d 101 /* 1440 x 900 [16:10] */
81dee67e 102 {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
3318bb5e
MC
103 FB_SYNC_VERT_HIGH_ACT,
104 FB_VMODE_NONINTERLACED},
81dee67e
SM
105
106 /* 1440x960 [15:10] */
107 {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
3318bb5e
MC
108 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
109 FB_VMODE_NONINTERLACED},
81dee67e
SM
110
111 /* 1920x1080 [16:9] */
112 {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
3318bb5e
MC
113 FB_SYNC_VERT_HIGH_ACT,
114 FB_VMODE_NONINTERLACED},
81dee67e
SM
115};
116
117
118
119
120/* no hardware cursor supported under version 2.6.10, kernel bug */
27fa159b 121static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
81dee67e 122{
27fa159b
MC
123 struct lynxfb_par *par;
124 struct lynxfb_crtc *crtc;
125 struct lynx_cursor *cursor;
81dee67e
SM
126
127 par = info->par;
128 crtc = &par->crtc;
129 cursor = &crtc->cursor;
130
5ace4e10 131 if (fbcursor->image.width > cursor->maxW ||
c52c3700 132 fbcursor->image.height > cursor->maxH ||
5ace4e10 133 fbcursor->image.depth > 1) {
81dee67e
SM
134 return -ENXIO;
135 }
136
137 cursor->disable(cursor);
f46a04c7 138 if (fbcursor->set & FB_CUR_SETSIZE)
3318bb5e
MC
139 cursor->setSize(cursor,
140 fbcursor->image.width,
141 fbcursor->image.height);
81dee67e 142
3318bb5e
MC
143 if (fbcursor->set & FB_CUR_SETPOS)
144 cursor->setPos(cursor,
145 fbcursor->image.dx - info->var.xoffset,
c52c3700 146 fbcursor->image.dy - info->var.yoffset);
81dee67e 147
5ace4e10 148 if (fbcursor->set & FB_CUR_SETCMAP) {
81dee67e 149 /* get the 16bit color of kernel means */
45e3b3da 150 u16 fg, bg;
876e5a70 151
81dee67e 152 fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))|
c52c3700
MC
153 ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)|
154 ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
81dee67e
SM
155
156 bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))|
c52c3700
MC
157 ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)|
158 ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
81dee67e 159
45e3b3da 160 cursor->setColor(cursor, fg, bg);
81dee67e
SM
161 }
162
163
5ace4e10 164 if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
81dee67e 165 cursor->setData(cursor,
c52c3700
MC
166 fbcursor->rop,
167 fbcursor->image.data,
168 fbcursor->mask);
81dee67e
SM
169 }
170
f46a04c7 171 if (fbcursor->enable)
81dee67e 172 cursor->enable(cursor);
81dee67e
SM
173
174 return 0;
175}
176
3318bb5e
MC
177static void lynxfb_ops_fillrect(struct fb_info *info,
178 const struct fb_fillrect *region)
81dee67e 179{
27fa159b
MC
180 struct lynxfb_par *par;
181 struct lynx_share *share;
45e3b3da 182 unsigned int base, pitch, Bpp, rop;
81dee67e
SM
183 u32 color;
184
f46a04c7 185 if (info->state != FBINFO_STATE_RUNNING)
81dee67e 186 return;
81dee67e
SM
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
eb1167a3 197 color = (Bpp == 1)?region->color:((u32 *)info->pseudo_palette)[region->color];
5ace4e10 198 rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR:HW_ROP2_COPY;
81dee67e 199
cb422f3b
LS
200 /*
201 * If not use spin_lock,system will die if user load driver
69e98df7 202 * and immediately unload driver frequently (dual)
cb422f3b
LS
203 */
204 if (share->dual)
205 spin_lock(&share->slock);
206
81dee67e 207 share->accel.de_fillrect(&share->accel,
45e3b3da
MC
208 base, pitch, Bpp,
209 region->dx, region->dy,
210 region->width, region->height,
211 color, rop);
cb422f3b
LS
212 if (share->dual)
213 spin_unlock(&share->slock);
81dee67e
SM
214}
215
3318bb5e
MC
216static void lynxfb_ops_copyarea(struct fb_info *info,
217 const struct fb_copyarea *region)
81dee67e 218{
27fa159b
MC
219 struct lynxfb_par *par;
220 struct lynx_share *share;
45e3b3da 221 unsigned int base, pitch, Bpp;
81dee67e
SM
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
cb422f3b
LS
232 /*
233 * If not use spin_lock, system will die if user load driver
69e98df7 234 * and immediately unload driver frequently (dual)
cb422f3b
LS
235 */
236 if (share->dual)
237 spin_lock(&share->slock);
238
81dee67e 239 share->accel.de_copyarea(&share->accel,
45e3b3da
MC
240 base, pitch, region->sx, region->sy,
241 base, pitch, Bpp, region->dx, region->dy,
242 region->width, region->height, HW_ROP2_COPY);
cb422f3b
LS
243 if (share->dual)
244 spin_unlock(&share->slock);
81dee67e
SM
245}
246
3318bb5e
MC
247static void lynxfb_ops_imageblit(struct fb_info *info,
248 const struct fb_image *image)
81dee67e 249{
45e3b3da
MC
250 unsigned int base, pitch, Bpp;
251 unsigned int fgcol, bgcol;
27fa159b
MC
252 struct lynxfb_par *par;
253 struct lynx_share *share;
81dee67e
SM
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
5ace4e10
MC
263 if (image->depth == 1) {
264 if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
876e5a70 265 info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
eb1167a3
MC
266 fgcol = ((u32 *)info->pseudo_palette)[image->fg_color];
267 bgcol = ((u32 *)info->pseudo_palette)[image->bg_color];
70407df7 268 } else {
81dee67e
SM
269 fgcol = image->fg_color;
270 bgcol = image->bg_color;
271 }
272 goto _do_work;
273 }
288ef567
HC
274 /* TODO: Implement hardware acceleration for image->depth > 1 */
275 cfb_imageblit(info, image);
81dee67e 276 return;
288ef567 277
81dee67e 278_do_work:
cb422f3b
LS
279 /*
280 * If not use spin_lock, system will die if user load driver
69e98df7 281 * and immediately unload driver frequently (dual)
cb422f3b
LS
282 */
283 if (share->dual)
284 spin_lock(&share->slock);
285
81dee67e 286 share->accel.de_imageblit(&share->accel,
45e3b3da
MC
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);
cb422f3b
LS
292 if (share->dual)
293 spin_unlock(&share->slock);
81dee67e
SM
294}
295
296static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
c52c3700 297 struct fb_info *info)
81dee67e 298{
27fa159b
MC
299 struct lynxfb_par *par;
300 struct lynxfb_crtc *crtc;
c52c3700 301 int ret;
81dee67e 302
81dee67e 303
5ace4e10 304 if (!info)
c52c3700 305 return -EINVAL;
81dee67e 306
c52c3700
MC
307 ret = 0;
308 par = info->par;
309 crtc = &par->crtc;
310 ret = crtc->proc_panDisplay(crtc, var, info);
311
312 return ret;
81dee67e
SM
313}
314
27fa159b 315static int lynxfb_ops_set_par(struct fb_info *info)
81dee67e 316{
27fa159b
MC
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;
81dee67e
SM
323 int ret;
324 unsigned int line_length;
81dee67e 325
5ace4e10 326 if (!info)
81dee67e
SM
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;
45e3b3da 339 line_length = PADDING(crtc->line_pad, line_length);
81dee67e 340 fix->line_length = line_length;
78cb7a38 341 pr_info("fix->line_length = %d\n", fix->line_length);
81dee67e
SM
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
5ace4e10 347 switch (var->bits_per_pixel) {
d6b0d6de
IC
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;
a0c838f1 376 var->blue.offset = 0;
d6b0d6de
IC
377 var->blue.length = 8;
378 fix->visual = FB_VISUAL_TRUECOLOR;
379 break;
380 default:
381 ret = -EINVAL;
382 break;
81dee67e
SM
383 }
384 var->height = var->width = -1;
385 var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
386
5ace4e10 387 if (ret) {
81dee67e
SM
388 pr_err("pixel bpp format not satisfied\n.");
389 return ret;
390 }
45e3b3da 391 ret = crtc->proc_setMode(crtc, var, fix);
5ace4e10 392 if (!ret)
45e3b3da 393 ret = output->proc_setMode(output, var, fix);
81dee67e
SM
394 return ret;
395}
848f2fce 396
3318bb5e
MC
397static inline unsigned int chan_to_field(unsigned int chan,
398 struct fb_bitfield *bf)
81dee67e
SM
399{
400 chan &= 0xffff;
401 chan >>= 16 - bf->length;
402 return chan << bf->offset;
403}
404
848f2fce
SM
405#ifdef CONFIG_PM
406static 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)
4bd9503d 428 /* 1 means do suspend */
848f2fce
SM
429 fb_set_suspend(info, 1);
430 info = share->fbinfo[1];
431 if (info)
4bd9503d 432 /* 1 means do suspend */
848f2fce
SM
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
4bd9503d 441 /* set chip to sleep mode */
848f2fce
SM
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}
81dee67e 457
27fa159b 458static int lynxfb_resume(struct pci_dev *pdev)
81dee67e 459{
27fa159b
MC
460 struct fb_info *info;
461 struct lynx_share *share;
81dee67e 462
27fa159b
MC
463 struct lynxfb_par *par;
464 struct lynxfb_crtc *crtc;
465 struct lynx_cursor *cursor;
81dee67e
SM
466
467 int ret;
c52c3700 468
81dee67e
SM
469
470 ret = 0;
471 share = pci_get_drvdata(pdev);
472
473 console_lock();
474
61c507cf
MC
475 ret = pci_set_power_state(pdev, PCI_D0);
476 if (ret) {
69e98df7 477 pr_err("error:%d occurred in pci_set_power_state\n", ret);
81dee67e
SM
478 return ret;
479 }
480
481
5ace4e10 482 if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) {
81dee67e 483 pci_restore_state(pdev);
61c507cf
MC
484 ret = pci_enable_device(pdev);
485 if (ret) {
69e98df7 486 pr_err("error:%d occurred in pci_enable_device\n", ret);
81dee67e
SM
487 return ret;
488 }
489 pci_set_master(pdev);
490 }
5ace4e10 491 if (share->resume)
81dee67e
SM
492 (*share->resume)(share);
493
45e3b3da 494 hw_sm750_inithw(share, pdev);
81dee67e
SM
495
496
497 info = share->fbinfo[0];
498
5ace4e10 499 if (info) {
81dee67e
SM
500 par = info->par;
501 crtc = &par->crtc;
502 cursor = &crtc->cursor;
3de08a2d
LS
503 memset_io(cursor->vstart, 0x0, cursor->size);
504 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
81dee67e
SM
505 lynxfb_ops_set_par(info);
506 fb_set_suspend(info, 0);
507 }
508
509 info = share->fbinfo[1];
510
5ace4e10 511 if (info) {
81dee67e
SM
512 par = info->par;
513 crtc = &par->crtc;
514 cursor = &crtc->cursor;
3de08a2d
LS
515 memset_io(cursor->vstart, 0x0, cursor->size);
516 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
81dee67e
SM
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
3318bb5e
MC
527static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
528 struct fb_info *info)
81dee67e 529{
27fa159b
MC
530 struct lynxfb_par *par;
531 struct lynxfb_crtc *crtc;
532 struct lynxfb_output *output;
533 struct lynx_share *share;
81dee67e
SM
534 int ret;
535 resource_size_t request;
536
c52c3700 537
81dee67e
SM
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",
c52c3700
MC
545 var->xres,
546 var->yres,
547 var->bits_per_pixel);
81dee67e
SM
548
549
5ace4e10 550 switch (var->bits_per_pixel) {
d6b0d6de
IC
551 case 8:
552 case 16:
553 case 24: /* support 24 bpp for only lynx712/722/720 */
554 case 32:
555 break;
556 default:
45e3b3da 557 pr_err("bpp %d not supported\n", var->bits_per_pixel);
d6b0d6de
IC
558 ret = -EINVAL;
559 goto exit;
81dee67e
SM
560 }
561
5ace4e10 562 switch (var->bits_per_pixel) {
d6b0d6de
IC
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;
a0c838f1 591 var->blue.offset = 0;
d6b0d6de
IC
592 var->blue.length = 8;
593 info->fix.visual = FB_VISUAL_TRUECOLOR;
594 break;
595 default:
596 ret = -EINVAL;
597 break;
81dee67e
SM
598 }
599 var->height = var->width = -1;
4bd9503d 600 var->accel_flags = 0;/* FB_ACCELF_TEXT; */
81dee67e 601
3318bb5e 602 /* check if current fb's video memory big enought to hold the onscreen*/
81dee67e
SM
603 request = var->xres_virtual * (var->bits_per_pixel >> 3);
604 /* defaulty crtc->channel go with par->index */
605
45e3b3da 606 request = PADDING(crtc->line_pad, request);
81dee67e 607 request = request * var->yres_virtual;
5ace4e10 608 if (crtc->vidmem_size < request) {
81dee67e
SM
609 pr_err("not enough video memory for mode\n");
610 return -ENOMEM;
611 }
612
45e3b3da 613 ret = output->proc_checkMode(output, var);
5ace4e10 614 if (!ret)
45e3b3da 615 ret = crtc->proc_checkMode(crtc, var);
81dee67e
SM
616exit:
617 return ret;
618}
619
620
876e5a70
MC
621static int lynxfb_ops_setcolreg(unsigned regno,
622 unsigned red,
623 unsigned green,
624 unsigned blue,
625 unsigned transp,
626 struct fb_info *info)
81dee67e 627{
27fa159b
MC
628 struct lynxfb_par *par;
629 struct lynxfb_crtc *crtc;
630 struct fb_var_screeninfo *var;
c52c3700
MC
631 int ret;
632
633 par = info->par;
634 crtc = &par->crtc;
635 var = &info->var;
636 ret = 0;
637
5ace4e10 638 if (regno > 256) {
45e3b3da 639 pr_err("regno = %d\n", regno);
c52c3700
MC
640 return -EINVAL;
641 }
642
5ace4e10 643 if (info->var.grayscale)
c52c3700
MC
644 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
645
3318bb5e
MC
646 if (var->bits_per_pixel == 8 &&
647 info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
c52c3700
MC
648 red >>= 8;
649 green >>= 8;
650 blue >>= 8;
45e3b3da 651 ret = crtc->proc_setColReg(crtc, regno, red, green, blue);
c52c3700
MC
652 goto exit;
653 }
654
655
5ace4e10 656 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
c52c3700 657 u32 val;
876e5a70 658
5ace4e10 659 if (var->bits_per_pixel == 16 ||
876e5a70
MC
660 var->bits_per_pixel == 32 ||
661 var->bits_per_pixel == 24) {
45e3b3da
MC
662 val = chan_to_field(red, &var->red);
663 val |= chan_to_field(green, &var->green);
664 val |= chan_to_field(blue, &var->blue);
c52c3700
MC
665 par->pseudo_palette[regno] = val;
666 goto exit;
667 }
668 }
669
670 ret = -EINVAL;
81dee67e
SM
671
672exit:
c52c3700 673 return ret;
81dee67e
SM
674}
675
27fa159b 676static int lynxfb_ops_blank(int blank, struct fb_info *info)
81dee67e 677{
27fa159b
MC
678 struct lynxfb_par *par;
679 struct lynxfb_output *output;
c52c3700 680
45e3b3da 681 pr_debug("blank = %d.\n", blank);
81dee67e
SM
682 par = info->par;
683 output = &par->output;
45e3b3da 684 return output->proc_setBLANK(output, blank);
81dee67e
SM
685}
686
27fa159b 687static int sm750fb_set_drv(struct lynxfb_par *par)
81dee67e 688{
c52c3700 689 int ret;
27fa159b
MC
690 struct lynx_share *share;
691 struct sm750_share *spec_share;
692 struct lynxfb_output *output;
693 struct lynxfb_crtc *crtc;
c52c3700
MC
694
695 ret = 0;
696
697 share = par->share;
45e3b3da 698 spec_share = container_of(share, struct sm750_share, share);
c52c3700
MC
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;
c52c3700
MC
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 */
a0c838f1 722 share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait : hw_sm750_deWait;
70407df7 723 switch (spec_share->state.dataflow) {
c52c3700
MC
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:
5ace4e10 738 if (par->index == 0) {
c52c3700
MC
739 output->paths = sm750_panel;
740 crtc->channel = sm750_primary;
741 crtc->oScreen = 0;
742 crtc->vScreen = share->pvMem;
5ace4e10 743 } else {
c52c3700
MC
744 output->paths = sm750_crt;
745 crtc->channel = sm750_secondary;
4bd9503d 746 /* not consider of padding stuffs for oScreen,need fix */
c52c3700
MC
747 crtc->oScreen = (share->vidmem_size >> 1);
748 crtc->vScreen = share->pvMem + crtc->oScreen;
749 }
750 break;
751 case sm750_dual_swap:
5ace4e10 752 if (par->index == 0) {
c52c3700
MC
753 output->paths = sm750_panel;
754 crtc->channel = sm750_secondary;
755 crtc->oScreen = 0;
756 crtc->vScreen = share->pvMem;
5ace4e10 757 } else {
c52c3700
MC
758 output->paths = sm750_crt;
759 crtc->channel = sm750_primary;
4bd9503d 760 /* not consider of padding stuffs for oScreen,need fix */
c52c3700
MC
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;
81dee67e
SM
770}
771
a0c838f1 772static struct fb_ops lynxfb_ops = {
81dee67e
SM
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,
81dee67e
SM
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
27fa159b 786static int lynxfb_set_fbinfo(struct fb_info *info, int index)
81dee67e 787{
c52c3700 788 int i;
27fa159b
MC
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[] = {
45e3b3da 797 lynx750_ext, NULL, vesa_modes,
c52c3700 798 };
45e3b3da 799 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
a0c838f1 800 static const char *mdb_desc[] = {
c52c3700
MC
801 "driver prepared modes",
802 "kernel prepared default modedb",
803 "kernel HELPERS prepared vesa_modes",
804 };
805
806
27fa159b 807 static const char *fixId[2] = {
45e3b3da 808 "sm750_fb1", "sm750_fb2",
c52c3700
MC
809 };
810
45e3b3da 811 int ret, line_length;
c52c3700
MC
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;
81dee67e 824 sm750fb_set_drv(par);
c52c3700
MC
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
45e3b3da 833 pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
c52c3700
MC
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);
5ace4e10 847 if (!g_hwcursor) {
c52c3700
MC
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 */
5ace4e10 854 if (!share->accel_off) {
c52c3700
MC
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
5ace4e10 862 if (!g_fbmode[index]) {
c52c3700 863 g_fbmode[index] = g_def_fbmode;
5ace4e10 864 if (index)
c52c3700
MC
865 g_fbmode[index] = g_fbmode[0];
866 }
81dee67e
SM
867
868
a0c838f1 869 for (i = 0; i < 3; i++) {
81dee67e 870
45e3b3da
MC
871 ret = fb_find_mode(var, info, g_fbmode[index],
872 pdb[i], cdb[i], NULL, 8);
81dee67e 873
5ace4e10 874 if (ret == 1) {
81dee67e 875 pr_info("success! use specified mode:%s in %s\n",
c52c3700
MC
876 g_fbmode[index],
877 mdb_desc[i]);
81dee67e 878 break;
5ace4e10 879 } else if (ret == 2) {
81dee67e 880 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
c52c3700
MC
881 g_fbmode[index],
882 mdb_desc[i]);
81dee67e 883 break;
5ace4e10 884 } else if (ret == 3) {
81dee67e 885 pr_warn("wanna use default mode\n");
4bd9503d 886 /*break;*/
5ace4e10 887 } else if (ret == 4) {
81dee67e 888 pr_warn("fall back to any valid mode\n");
5ace4e10 889 } else {
3318bb5e
MC
890 pr_warn("ret = %d,fb_find_mode failed,with %s\n",
891 ret,
892 mdb_desc[i]);
81dee67e
SM
893 }
894 }
895
c52c3700
MC
896 /* some member of info->var had been set by fb_find_mode */
897
898 pr_info("Member of info->var is :\n\
3318bb5e
MC
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);
c52c3700
MC
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;
45e3b3da 924 pr_debug("screen_base vaddr = %p\n", info->screen_base);
81dee67e
SM
925 info->screen_size = line_length * var->yres_virtual;
926 info->flags = FBINFO_FLAG_DEFAULT|0;
927
c52c3700
MC
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;
81dee67e 935
45e3b3da 936 strlcpy(fix->id, fixId[index], sizeof(fix->id));
81dee67e
SM
937
938
939 fix->smem_start = crtc->oScreen + share->vidmem_start;
45e3b3da 940 pr_info("fix->smem_start = %lx\n", fix->smem_start);
c52c3700
MC
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;
45e3b3da 948 pr_info("fix->smem_len = %x\n", fix->smem_len);
c52c3700
MC
949 info->screen_size = fix->smem_len;
950 fix->line_length = line_length;
951 fix->mmio_start = share->vidreg_start;
45e3b3da 952 pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
c52c3700 953 fix->mmio_len = share->vidreg_size;
45e3b3da 954 pr_info("fix->mmio_len = %x\n", fix->mmio_len);
5ace4e10 955 switch (var->bits_per_pixel) {
c52c3700
MC
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",
45e3b3da
MC
971 info->cmap.start, info->cmap.len,
972 info->cmap.red, info->cmap.green, info->cmap.blue,
c52c3700
MC
973 info->cmap.transp);
974
61c507cf
MC
975 ret = fb_alloc_cmap(&info->cmap, 256, 0);
976 if (ret < 0) {
00827207 977 pr_err("Could not allocate memory for cmap.\n");
c52c3700
MC
978 goto exit;
979 }
980
a0c838f1 981 pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
45e3b3da
MC
982 info->cmap.start, info->cmap.len,
983 info->cmap.red, info->cmap.green, info->cmap.blue,
c52c3700 984 info->cmap.transp);
81dee67e
SM
985
986exit:
45e3b3da 987 lynxfb_ops_check_var(var, info);
c52c3700 988 return ret;
81dee67e
SM
989}
990
c52c3700 991/* chip specific g_option configuration routine */
27fa159b 992static void sm750fb_setup(struct lynx_share *share, char *src)
81dee67e 993{
27fa159b
MC
994 struct sm750_share *spec_share;
995 char *opt;
81dee67e 996#ifdef CAP_EXPENSION
27fa159b 997 char *exp_res;
81dee67e
SM
998#endif
999 int swap;
c52c3700 1000
81dee67e 1001
45e3b3da 1002 spec_share = container_of(share, struct sm750_share, share);
81dee67e 1003#ifdef CAP_EXPENSIION
c52c3700 1004 exp_res = NULL;
81dee67e 1005#endif
c52c3700
MC
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
4bd9503d 1015 /* defaultly turn g_hwcursor on for both view */
c52c3700
MC
1016 g_hwcursor = 3;
1017
5ace4e10 1018 if (!src || !*src) {
c52c3700
MC
1019 pr_warn("no specific g_option.\n");
1020 goto NO_PARAM;
1021 }
1022
5ace4e10 1023 while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
78cb7a38
HPGE
1024 pr_info("opt=%s\n", opt);
1025 pr_info("src=%s\n", src);
c52c3700 1026
5ace4e10 1027 if (!strncmp(opt, "swap", strlen("swap")))
c52c3700 1028 swap = 1;
5ace4e10 1029 else if (!strncmp(opt, "nocrt", strlen("nocrt")))
c52c3700 1030 spec_share->state.nocrt = 1;
5ace4e10 1031 else if (!strncmp(opt, "36bit", strlen("36bit")))
c52c3700 1032 spec_share->state.pnltype = sm750_doubleTFT;
5ace4e10 1033 else if (!strncmp(opt, "18bit", strlen("18bit")))
c52c3700 1034 spec_share->state.pnltype = sm750_dualTFT;
5ace4e10 1035 else if (!strncmp(opt, "24bit", strlen("24bit")))
c52c3700 1036 spec_share->state.pnltype = sm750_24TFT;
81dee67e 1037#ifdef CAP_EXPANSION
5ace4e10 1038 else if (!strncmp(opt, "exp:", strlen("exp:")))
c52c3700 1039 exp_res = opt + strlen("exp:");
81dee67e 1040#endif
5ace4e10 1041 else if (!strncmp(opt, "nohwc0", strlen("nohwc0")))
c52c3700 1042 g_hwcursor &= ~0x1;
5ace4e10 1043 else if (!strncmp(opt, "nohwc1", strlen("nohwc1")))
c52c3700 1044 g_hwcursor &= ~0x2;
5ace4e10 1045 else if (!strncmp(opt, "nohwc", strlen("nohwc")))
c52c3700 1046 g_hwcursor = 0;
70407df7 1047 else {
5ace4e10 1048 if (!g_fbmode[0]) {
c52c3700 1049 g_fbmode[0] = opt;
45e3b3da 1050 pr_info("find fbmode0 : %s\n", g_fbmode[0]);
5ace4e10 1051 } else if (!g_fbmode[1]) {
c52c3700 1052 g_fbmode[1] = opt;
45e3b3da 1053 pr_info("find fbmode1 : %s\n", g_fbmode[1]);
5ace4e10 1054 } else {
c52c3700
MC
1055 pr_warn("How many view you wann set?\n");
1056 }
1057 }
1058 }
81dee67e 1059#ifdef CAP_EXPANSION
3318bb5e
MC
1060 if (getExpRes(exp_res,
1061 &spec_share->state.xLCD,
1062 &spec_share->state.yLCD)) {
4bd9503d 1063 /* seems exp_res is not valid */
c52c3700
MC
1064 spec_share->state.xLCD = spec_share->state.yLCD = 0;
1065 }
81dee67e
SM
1066#endif
1067
1068NO_PARAM:
5ace4e10
MC
1069 if (share->revid != SM750LE_REVISION_ID) {
1070 if (share->dual) {
1071 if (swap)
c52c3700
MC
1072 spec_share->state.dataflow = sm750_dual_swap;
1073 else
1074 spec_share->state.dataflow = sm750_dual_normal;
5ace4e10
MC
1075 } else {
1076 if (swap)
c52c3700
MC
1077 spec_share->state.dataflow = sm750_simul_sec;
1078 else
1079 spec_share->state.dataflow = sm750_simul_pri;
1080 }
5ace4e10 1081 } else {
c52c3700
MC
1082 /* SM750LE only have one crt channel */
1083 spec_share->state.dataflow = sm750_simul_sec;
4bd9503d 1084 /* sm750le do not have complex attributes */
c52c3700
MC
1085 spec_share->state.nocrt = 0;
1086 }
81dee67e
SM
1087}
1088
27fa159b 1089static int lynxfb_pci_probe(struct pci_dev *pdev,
c52c3700 1090 const struct pci_device_id * ent)
81dee67e 1091{
27fa159b
MC
1092 struct fb_info *info[] = {NULL, NULL};
1093 struct lynx_share *share = NULL;
81dee67e
SM
1094
1095 struct sm750_share *spec_share = NULL;
1096 size_t spec_offset = 0;
1097 int fbidx;
c52c3700 1098
81dee67e
SM
1099
1100 /* enable device */
5ace4e10 1101 if (pci_enable_device(pdev)) {
81dee67e
SM
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 */
45e3b3da 1108 spec_offset = offsetof(struct sm750_share, share);
81dee67e 1109
45e3b3da 1110 spec_share = kzalloc(sizeof(*spec_share), GFP_KERNEL);
5ace4e10 1111 if (!spec_share) {
81dee67e
SM
1112 pr_err("Could not allocate memory for share.\n");
1113 goto err_share;
1114 }
1115
1116 /* setting share structure */
5ace4e10 1117 share = (struct lynx_share *)(&(spec_share->share));
81dee67e
SM
1118 share->fbinfo[0] = share->fbinfo[1] = NULL;
1119 share->devid = pdev->device;
1120 share->revid = pdev->revision;
1121
45e3b3da 1122 pr_info("share->revid = %02x\n", share->revid);
81dee67e 1123 share->pdev = pdev;
81dee67e
SM
1124 share->mtrr_off = g_nomtrr;
1125 share->mtrr.vram = 0;
81dee67e
SM
1126 share->accel_off = g_noaccel;
1127 share->dual = g_dualview;
1128 spin_lock_init(&share->slock);
1129
5ace4e10 1130 if (!share->accel_off) {
81dee67e
SM
1131 /* hook deInit and 2d routines, notes that below hw_xxx
1132 * routine can work on most of lynx chips
3318bb5e
MC
1133 * if some chip need specific function,
1134 * please hook it in smXXX_set_drv routine */
81dee67e
SM
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");
5ace4e10 1140 } else {
81dee67e
SM
1141 pr_info("disable 2d acceleration\n");
1142 }
1143
1144 /* call chip specific setup routine */
45e3b3da 1145 sm750fb_setup(share, g_settings);
81dee67e
SM
1146
1147 /* call chip specific mmap routine */
5ace4e10 1148 if (hw_sm750_map(share, pdev)) {
81dee67e
SM
1149 pr_err("Memory map failed\n");
1150 goto err_map;
1151 }
1152
2e043a92
LR
1153 if (!share->mtrr_off)
1154 share->mtrr.vram = arch_phys_wc_add(share->vidmem_start,
1155 share->vidmem_size);
81dee67e 1156
3de08a2d 1157 memset_io(share->pvMem, 0, share->vidmem_size);
81dee67e 1158
45e3b3da 1159 pr_info("sm%3x mmio address = %p\n", share->devid, share->pvReg);
81dee67e 1160
45e3b3da 1161 pci_set_drvdata(pdev, share);
81dee67e
SM
1162
1163 /* call chipInit routine */
45e3b3da 1164 hw_sm750_inithw(share, pdev);
81dee67e
SM
1165
1166 /* allocate frame buffer info structor according to g_dualview */
c52c3700 1167 fbidx = 0;
81dee67e 1168ALLOC_FB:
45e3b3da 1169 info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev);
5ace4e10 1170 if (!info[fbidx]) {
45e3b3da 1171 pr_err("Could not allocate framebuffer #%d.\n", fbidx);
5ace4e10 1172 if (fbidx == 0)
c52c3700
MC
1173 goto err_info0_alloc;
1174 else
1175 goto err_info1_alloc;
70407df7 1176 } else {
27fa159b 1177 struct lynxfb_par *par;
c52c3700 1178 int errno;
876e5a70 1179
45e3b3da 1180 pr_info("framebuffer #%d alloc okay\n", fbidx);
c52c3700
MC
1181 share->fbinfo[fbidx] = info[fbidx];
1182 par = info[fbidx]->par;
1183 par->share = share;
1184
1185 /* set fb_info structure */
5ace4e10 1186 if (lynxfb_set_fbinfo(info[fbidx], fbidx)) {
45e3b3da 1187 pr_err("Failed to initial fb_info #%d.\n", fbidx);
5ace4e10 1188 if (fbidx == 0)
c52c3700 1189 goto err_info0_set;
81dee67e 1190 else
c52c3700 1191 goto err_info1_set;
81dee67e 1192 }
81dee67e 1193
4bd9503d 1194 /* register frame buffer */
45e3b3da 1195 pr_info("Ready to register framebuffer #%d.\n", fbidx);
c52c3700
MC
1196 errno = register_framebuffer(info[fbidx]);
1197 if (errno < 0) {
3318bb5e 1198 pr_err("Failed to register fb_info #%d. err %d\n",
876e5a70
MC
1199 fbidx,
1200 errno);
5ace4e10 1201 if (fbidx == 0)
c52c3700
MC
1202 goto err_register0;
1203 else
1204 goto err_register1;
81dee67e 1205 }
45e3b3da 1206 pr_info("Accomplished register framebuffer #%d.\n", fbidx);
c52c3700 1207 }
81dee67e 1208
c52c3700
MC
1209 /* no dual view by far */
1210 fbidx++;
5ace4e10 1211 if (share->dual && fbidx < 2)
c52c3700 1212 goto ALLOC_FB;
81dee67e
SM
1213
1214 return 0;
1215
1216err_register1:
1217err_info1_set:
1218 framebuffer_release(info[1]);
1219err_info1_alloc:
1220 unregister_framebuffer(info[0]);
1221err_register0:
1222err_info0_set:
1223 framebuffer_release(info[0]);
1224err_info0_alloc:
1225err_map:
1226 kfree(spec_share);
1227err_share:
1228err_enable:
1229 return -ENODEV;
1230}
1231
bb6ce8b2 1232static void lynxfb_pci_remove(struct pci_dev *pdev)
81dee67e 1233{
27fa159b
MC
1234 struct fb_info *info;
1235 struct lynx_share *share;
1236 void *spec_share;
1237 struct lynxfb_par *par;
81dee67e
SM
1238 int cnt;
1239
1240 cnt = 2;
1241 share = pci_get_drvdata(pdev);
1242
5ace4e10 1243 while (cnt-- > 0) {
81dee67e 1244 info = share->fbinfo[cnt];
5ace4e10 1245 if (!info)
81dee67e
SM
1246 continue;
1247 par = info->par;
1248
1249 unregister_framebuffer(info);
4bd9503d 1250 /* clean crtc & output allocations */
81dee67e
SM
1251 par->crtc.clear(&par->crtc);
1252 par->output.clear(&par->output);
4bd9503d 1253 /* release frame buffer */
81dee67e
SM
1254 framebuffer_release(info);
1255 }
2e043a92 1256 arch_phys_wc_del(share->mtrr.vram);
81dee67e
SM
1257
1258 iounmap(share->pvReg);
1259 iounmap(share->pvMem);
45e3b3da 1260 spec_share = container_of(share, struct sm750_share, share);
81dee67e
SM
1261 kfree(g_settings);
1262 kfree(spec_share);
45e3b3da 1263 pci_set_drvdata(pdev, NULL);
81dee67e
SM
1264}
1265
27fa159b 1266static int __init lynxfb_setup(char *options)
81dee67e
SM
1267{
1268 int len;
27fa159b 1269 char *opt, *tmp;
c52c3700 1270
81dee67e 1271
5ace4e10 1272 if (!options || !*options) {
81dee67e
SM
1273 pr_warn("no options.\n");
1274 return 0;
1275 }
1276
45e3b3da 1277 pr_info("options:%s\n", options);
81dee67e
SM
1278
1279 len = strlen(options) + 1;
a99e334d 1280 g_settings = kzalloc(len, GFP_KERNEL);
5ace4e10 1281 if (!g_settings)
81dee67e
SM
1282 return -ENOMEM;
1283
81dee67e
SM
1284 tmp = g_settings;
1285
c52c3700 1286 /* Notes:
81dee67e
SM
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 */
a0c838f1 1294 while ((opt = strsep(&options, ":")) != NULL) {
81dee67e 1295 /* options that mean for any lynx chips are configured here */
5ace4e10 1296 if (!strncmp(opt, "noaccel", strlen("noaccel")))
81dee67e 1297 g_noaccel = 1;
5ace4e10 1298 else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
81dee67e 1299 g_nomtrr = 1;
5ace4e10 1300 else if (!strncmp(opt, "dual", strlen("dual")))
81dee67e 1301 g_dualview = 1;
70407df7 1302 else {
45e3b3da 1303 strcat(tmp, opt);
81dee67e 1304 tmp += strlen(opt);
5ace4e10 1305 if (options != NULL)
81dee67e
SM
1306 *tmp++ = ':';
1307 else
1308 *tmp++ = 0;
1309 }
1310 }
1311
1312 /* misc g_settings are transport to chip specific routines */
45e3b3da 1313 pr_info("parameter left for chip specific analysis:%s\n", g_settings);
81dee67e
SM
1314 return 0;
1315}
1316
1317static struct pci_device_id smi_pci_table[] = {
1318 { PCI_DEVICE(0x126f, 0x0750), },
1319 {0,}
1320};
1321
45e3b3da 1322MODULE_DEVICE_TABLE(pci, smi_pci_table);
81dee67e
SM
1323
1324static 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
1336static int __init lynxfb_init(void)
1337{
a0c838f1 1338 char *option;
81dee67e
SM
1339 int ret;
1340
1341#ifdef MODULE
1342 option = g_option;
1343#else
5ace4e10 1344 if (fb_get_options("sm750fb", &option))
81dee67e
SM
1345 return -ENODEV;
1346#endif
1347
1348 lynxfb_setup(option);
1349 ret = pci_register_driver(&lynxfb_driver);
1350 return ret;
1351}
1352module_init(lynxfb_init);
1353
1354static void __exit lynxfb_exit(void)
1355{
1356 pci_unregister_driver(&lynxfb_driver);
1357}
1358module_exit(lynxfb_exit);
1359
45e3b3da 1360module_param(g_option, charp, S_IRUGO);
81dee67e
SM
1361
1362MODULE_PARM_DESC(g_option,
c52c3700
MC
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 );
81dee67e
SM
1371
1372MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1373MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1374MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1375MODULE_LICENSE("GPL v2");