sm750.c 31 KB

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