sm750.c 30 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 = "800x600-16@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. hw_cursor_disable(cursor);
  103. if (fbcursor->set & FB_CUR_SETSIZE)
  104. hw_cursor_setSize(cursor,
  105. fbcursor->image.width,
  106. fbcursor->image.height);
  107. if (fbcursor->set & FB_CUR_SETPOS)
  108. 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. hw_cursor_setColor(cursor, fg, bg);
  121. }
  122. if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
  123. hw_cursor_setData(cursor,
  124. fbcursor->rop,
  125. fbcursor->image.data,
  126. fbcursor->mask);
  127. }
  128. if (fbcursor->enable)
  129. 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. */
  157. if (sm750_dev->fb_count > 1)
  158. spin_lock(&sm750_dev->slock);
  159. sm750_dev->accel.de_fillrect(&sm750_dev->accel,
  160. base, pitch, Bpp,
  161. region->dx, region->dy,
  162. region->width, region->height,
  163. color, rop);
  164. if (sm750_dev->fb_count > 1)
  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. */
  186. if (sm750_dev->fb_count > 1)
  187. spin_lock(&sm750_dev->slock);
  188. sm750_dev->accel.de_copyarea(&sm750_dev->accel,
  189. base, pitch, region->sx, region->sy,
  190. base, pitch, Bpp, region->dx, region->dy,
  191. region->width, region->height,
  192. HW_ROP2_COPY);
  193. if (sm750_dev->fb_count > 1)
  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. */
  229. if (sm750_dev->fb_count > 1)
  230. spin_lock(&sm750_dev->slock);
  231. sm750_dev->accel.de_imageblit(&sm750_dev->accel,
  232. image->data, image->width >> 3, 0,
  233. base, pitch, Bpp,
  234. image->dx, image->dy,
  235. image->width, image->height,
  236. fgcol, bgcol, HW_ROP2_COPY);
  237. if (sm750_dev->fb_count > 1)
  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. return ret;
  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. return ret;
  370. }
  371. }
  372. pdev->dev.power.power_state = mesg;
  373. console_unlock();
  374. return ret;
  375. }
  376. static int lynxfb_resume(struct pci_dev *pdev)
  377. {
  378. struct fb_info *info;
  379. struct sm750_dev *sm750_dev;
  380. struct lynxfb_par *par;
  381. struct lynxfb_crtc *crtc;
  382. struct lynx_cursor *cursor;
  383. int ret;
  384. ret = 0;
  385. sm750_dev = pci_get_drvdata(pdev);
  386. console_lock();
  387. ret = pci_set_power_state(pdev, PCI_D0);
  388. if (ret) {
  389. dev_err(&pdev->dev,
  390. "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. dev_err(&pdev->dev,
  398. "error:%d occurred in pci_enable_device\n",
  399. ret);
  400. return ret;
  401. }
  402. pci_set_master(pdev);
  403. }
  404. hw_sm750_inithw(sm750_dev, pdev);
  405. info = sm750_dev->fbinfo[0];
  406. if (info) {
  407. par = info->par;
  408. crtc = &par->crtc;
  409. cursor = &crtc->cursor;
  410. memset_io(cursor->vstart, 0x0, cursor->size);
  411. memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
  412. lynxfb_ops_set_par(info);
  413. fb_set_suspend(info, 0);
  414. }
  415. info = sm750_dev->fbinfo[1];
  416. if (info) {
  417. par = info->par;
  418. crtc = &par->crtc;
  419. cursor = &crtc->cursor;
  420. memset_io(cursor->vstart, 0x0, cursor->size);
  421. memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
  422. lynxfb_ops_set_par(info);
  423. fb_set_suspend(info, 0);
  424. }
  425. pdev->dev.power.power_state.event = PM_EVENT_RESUME;
  426. console_unlock();
  427. return ret;
  428. }
  429. #endif
  430. static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
  431. struct fb_info *info)
  432. {
  433. struct lynxfb_par *par;
  434. struct lynxfb_crtc *crtc;
  435. struct lynxfb_output *output;
  436. resource_size_t request;
  437. par = info->par;
  438. crtc = &par->crtc;
  439. output = &par->output;
  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 regno,
  495. unsigned red,
  496. unsigned green,
  497. unsigned blue,
  498. unsigned 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. 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. pr_warn("no specific g_option.\n");
  816. goto NO_PARAM;
  817. }
  818. while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
  819. pr_info("opt=%s\n", opt);
  820. pr_info("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. pr_info("find fbmode0 : %s\n", g_fbmode[0]);
  841. } else if (!g_fbmode[1]) {
  842. g_fbmode[1] = opt;
  843. pr_info("find fbmode1 : %s\n", g_fbmode[1]);
  844. } else {
  845. pr_warn("How many view you wann set?\n");
  846. }
  847. }
  848. }
  849. NO_PARAM:
  850. if (sm750_dev->revid != SM750LE_REVISION_ID) {
  851. if (sm750_dev->fb_count > 1) {
  852. if (swap)
  853. sm750_dev->dataflow = sm750_dual_swap;
  854. else
  855. sm750_dev->dataflow = sm750_dual_normal;
  856. } else {
  857. if (swap)
  858. sm750_dev->dataflow = sm750_simul_sec;
  859. else
  860. sm750_dev->dataflow = sm750_simul_pri;
  861. }
  862. } else {
  863. /* SM750LE only have one crt channel */
  864. sm750_dev->dataflow = sm750_simul_sec;
  865. /* sm750le do not have complex attributes */
  866. sm750_dev->nocrt = 0;
  867. }
  868. }
  869. static void sm750fb_frambuffer_release(struct sm750_dev *sm750_dev)
  870. {
  871. struct fb_info *fb_info;
  872. while (sm750_dev->fb_count) {
  873. fb_info = sm750_dev->fbinfo[sm750_dev->fb_count - 1];
  874. unregister_framebuffer(fb_info);
  875. framebuffer_release(fb_info);
  876. sm750_dev->fb_count--;
  877. }
  878. }
  879. static int sm750fb_frambuffer_alloc(struct sm750_dev *sm750_dev, int fbidx)
  880. {
  881. struct fb_info *fb_info;
  882. struct lynxfb_par *par;
  883. int err;
  884. fb_info = framebuffer_alloc(sizeof(struct lynxfb_par),
  885. &sm750_dev->pdev->dev);
  886. if (!fb_info)
  887. return -ENOMEM;
  888. sm750_dev->fbinfo[fbidx] = fb_info;
  889. par = fb_info->par;
  890. par->dev = sm750_dev;
  891. err = lynxfb_set_fbinfo(fb_info, fbidx);
  892. if (err)
  893. goto release_fb;
  894. err = register_framebuffer(fb_info);
  895. if (err < 0)
  896. goto release_fb;
  897. sm750_dev->fb_count++;
  898. return 0;
  899. release_fb:
  900. framebuffer_release(fb_info);
  901. return err;
  902. }
  903. static int lynxfb_kick_out_firmware_fb(struct pci_dev *pdev)
  904. {
  905. struct apertures_struct *ap;
  906. bool primary = false;
  907. ap = alloc_apertures(1);
  908. if (!ap)
  909. return -ENOMEM;
  910. ap->ranges[0].base = pci_resource_start(pdev, 0);
  911. ap->ranges[0].size = pci_resource_len(pdev, 0);
  912. #ifdef CONFIG_X86
  913. primary = pdev->resource[PCI_ROM_RESOURCE].flags &
  914. IORESOURCE_ROM_SHADOW;
  915. #endif
  916. remove_conflicting_framebuffers(ap, "sm750_fb1", primary);
  917. kfree(ap);
  918. return 0;
  919. }
  920. static int lynxfb_pci_probe(struct pci_dev *pdev,
  921. const struct pci_device_id *ent)
  922. {
  923. struct sm750_dev *sm750_dev = NULL;
  924. int max_fb;
  925. int fbidx;
  926. int err;
  927. err = lynxfb_kick_out_firmware_fb(pdev);
  928. if (err)
  929. return err;
  930. /* enable device */
  931. err = pcim_enable_device(pdev);
  932. if (err)
  933. return err;
  934. err = -ENOMEM;
  935. sm750_dev = devm_kzalloc(&pdev->dev, sizeof(*sm750_dev), GFP_KERNEL);
  936. if (!sm750_dev)
  937. return err;
  938. sm750_dev->fbinfo[0] = sm750_dev->fbinfo[1] = NULL;
  939. sm750_dev->devid = pdev->device;
  940. sm750_dev->revid = pdev->revision;
  941. sm750_dev->pdev = pdev;
  942. sm750_dev->mtrr_off = g_nomtrr;
  943. sm750_dev->mtrr.vram = 0;
  944. sm750_dev->accel_off = g_noaccel;
  945. spin_lock_init(&sm750_dev->slock);
  946. if (!sm750_dev->accel_off) {
  947. /*
  948. * hook deInit and 2d routines, notes that below hw_xxx
  949. * routine can work on most of lynx chips
  950. * if some chip need specific function,
  951. * please hook it in smXXX_set_drv routine
  952. */
  953. sm750_dev->accel.de_init = hw_de_init;
  954. sm750_dev->accel.de_fillrect = hw_fillrect;
  955. sm750_dev->accel.de_copyarea = hw_copyarea;
  956. sm750_dev->accel.de_imageblit = hw_imageblit;
  957. }
  958. /* call chip specific setup routine */
  959. sm750fb_setup(sm750_dev, g_settings);
  960. /* call chip specific mmap routine */
  961. err = hw_sm750_map(sm750_dev, pdev);
  962. if (err)
  963. return err;
  964. if (!sm750_dev->mtrr_off)
  965. sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start,
  966. sm750_dev->vidmem_size);
  967. memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size);
  968. pci_set_drvdata(pdev, sm750_dev);
  969. /* call chipInit routine */
  970. hw_sm750_inithw(sm750_dev, pdev);
  971. /* allocate frame buffer info structures according to g_dualview */
  972. max_fb = g_dualview ? 2 : 1;
  973. for (fbidx = 0; fbidx < max_fb; fbidx++) {
  974. err = sm750fb_frambuffer_alloc(sm750_dev, fbidx);
  975. if (err)
  976. goto release_fb;
  977. }
  978. return 0;
  979. release_fb:
  980. sm750fb_frambuffer_release(sm750_dev);
  981. return err;
  982. }
  983. static void lynxfb_pci_remove(struct pci_dev *pdev)
  984. {
  985. struct sm750_dev *sm750_dev;
  986. sm750_dev = pci_get_drvdata(pdev);
  987. sm750fb_frambuffer_release(sm750_dev);
  988. arch_phys_wc_del(sm750_dev->mtrr.vram);
  989. iounmap(sm750_dev->pvReg);
  990. iounmap(sm750_dev->pvMem);
  991. kfree(g_settings);
  992. }
  993. static int __init lynxfb_setup(char *options)
  994. {
  995. int len;
  996. char *opt, *tmp;
  997. if (!options || !*options) {
  998. pr_warn("no options.\n");
  999. return 0;
  1000. }
  1001. pr_info("options:%s\n", options);
  1002. len = strlen(options) + 1;
  1003. g_settings = kzalloc(len, GFP_KERNEL);
  1004. if (!g_settings)
  1005. return -ENOMEM;
  1006. tmp = g_settings;
  1007. /*
  1008. * Notes:
  1009. * char * strsep(char **s,const char * ct);
  1010. * @s: the string to be searched
  1011. * @ct :the characters to search for
  1012. *
  1013. * strsep() updates @options to pointer after the first found token
  1014. * it also returns the pointer ahead the token.
  1015. */
  1016. while ((opt = strsep(&options, ":")) != NULL) {
  1017. /* options that mean for any lynx chips are configured here */
  1018. if (!strncmp(opt, "noaccel", strlen("noaccel")))
  1019. g_noaccel = 1;
  1020. else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
  1021. g_nomtrr = 1;
  1022. else if (!strncmp(opt, "dual", strlen("dual")))
  1023. g_dualview = 1;
  1024. else {
  1025. strcat(tmp, opt);
  1026. tmp += strlen(opt);
  1027. if (options)
  1028. *tmp++ = ':';
  1029. else
  1030. *tmp++ = 0;
  1031. }
  1032. }
  1033. /* misc g_settings are transport to chip specific routines */
  1034. pr_info("parameter left for chip specific analysis:%s\n", g_settings);
  1035. return 0;
  1036. }
  1037. static struct pci_device_id smi_pci_table[] = {
  1038. { PCI_DEVICE(0x126f, 0x0750), },
  1039. {0,}
  1040. };
  1041. MODULE_DEVICE_TABLE(pci, smi_pci_table);
  1042. static struct pci_driver lynxfb_driver = {
  1043. .name = "sm750fb",
  1044. .id_table = smi_pci_table,
  1045. .probe = lynxfb_pci_probe,
  1046. .remove = lynxfb_pci_remove,
  1047. #ifdef CONFIG_PM
  1048. .suspend = lynxfb_suspend,
  1049. .resume = lynxfb_resume,
  1050. #endif
  1051. };
  1052. static int __init lynxfb_init(void)
  1053. {
  1054. char *option;
  1055. int ret;
  1056. #ifdef MODULE
  1057. option = g_option;
  1058. #else
  1059. if (fb_get_options("sm750fb", &option))
  1060. return -ENODEV;
  1061. #endif
  1062. lynxfb_setup(option);
  1063. ret = pci_register_driver(&lynxfb_driver);
  1064. return ret;
  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, S_IRUGO);
  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("GPL v2");