sm750.c 31 KB

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