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