fbmon.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /*
  2. * linux/drivers/video/fbmon.c
  3. *
  4. * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
  5. *
  6. * Credits:
  7. *
  8. * The EDID Parser is a conglomeration from the following sources:
  9. *
  10. * 1. SciTech SNAP Graphics Architecture
  11. * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
  12. *
  13. * 2. XFree86 4.3.0, interpret_edid.c
  14. * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
  15. *
  16. * 3. John Fremlin <vii@users.sourceforge.net> and
  17. * Ani Joshi <ajoshi@unixbox.com>
  18. *
  19. * Generalized Timing Formula is derived from:
  20. *
  21. * GTF Spreadsheet by Andy Morrish (1/5/97)
  22. * available at http://www.vesa.org
  23. *
  24. * This file is subject to the terms and conditions of the GNU General Public
  25. * License. See the file COPYING in the main directory of this archive
  26. * for more details.
  27. *
  28. */
  29. #include <linux/tty.h>
  30. #include <linux/fb.h>
  31. #include <linux/module.h>
  32. #ifdef CONFIG_PPC_OF
  33. #include <linux/pci.h>
  34. #include <asm/prom.h>
  35. #include <asm/pci-bridge.h>
  36. #endif
  37. #include "edid.h"
  38. /*
  39. * EDID parser
  40. */
  41. #undef DEBUG /* define this for verbose EDID parsing output */
  42. #ifdef DEBUG
  43. #define DPRINTK(fmt, args...) printk(fmt,## args)
  44. #else
  45. #define DPRINTK(fmt, args...)
  46. #endif
  47. #define FBMON_FIX_HEADER 1
  48. #define FBMON_FIX_INPUT 2
  49. #ifdef CONFIG_FB_MODE_HELPERS
  50. struct broken_edid {
  51. u8 manufacturer[4];
  52. u32 model;
  53. u32 fix;
  54. };
  55. static struct broken_edid brokendb[] = {
  56. /* DEC FR-PCXAV-YZ */
  57. {
  58. .manufacturer = "DEC",
  59. .model = 0x073a,
  60. .fix = FBMON_FIX_HEADER,
  61. },
  62. /* ViewSonic PF775a */
  63. {
  64. .manufacturer = "VSC",
  65. .model = 0x5a44,
  66. .fix = FBMON_FIX_INPUT,
  67. },
  68. };
  69. static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
  70. 0xff, 0xff, 0xff, 0x00
  71. };
  72. static void copy_string(unsigned char *c, unsigned char *s)
  73. {
  74. int i;
  75. c = c + 5;
  76. for (i = 0; (i < 13 && *c != 0x0A); i++)
  77. *(s++) = *(c++);
  78. *s = 0;
  79. while (i-- && (*--s == 0x20)) *s = 0;
  80. }
  81. static int check_edid(unsigned char *edid)
  82. {
  83. unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
  84. unsigned char *b;
  85. u32 model;
  86. int i, fix = 0, ret = 0;
  87. manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
  88. manufacturer[1] = ((block[0] & 0x03) << 3) +
  89. ((block[1] & 0xe0) >> 5) + '@';
  90. manufacturer[2] = (block[1] & 0x1f) + '@';
  91. manufacturer[3] = 0;
  92. model = block[2] + (block[3] << 8);
  93. for (i = 0; i < ARRAY_SIZE(brokendb); i++) {
  94. if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) &&
  95. brokendb[i].model == model) {
  96. printk("fbmon: The EDID Block of "
  97. "Manufacturer: %s Model: 0x%x is known to "
  98. "be broken,\n", manufacturer, model);
  99. fix = brokendb[i].fix;
  100. break;
  101. }
  102. }
  103. switch (fix) {
  104. case FBMON_FIX_HEADER:
  105. for (i = 0; i < 8; i++) {
  106. if (edid[i] != edid_v1_header[i])
  107. ret = fix;
  108. }
  109. break;
  110. case FBMON_FIX_INPUT:
  111. b = edid + EDID_STRUCT_DISPLAY;
  112. /* Only if display is GTF capable will
  113. the input type be reset to analog */
  114. if (b[4] & 0x01 && b[0] & 0x80)
  115. ret = fix;
  116. break;
  117. }
  118. return ret;
  119. }
  120. static void fix_edid(unsigned char *edid, int fix)
  121. {
  122. unsigned char *b;
  123. switch (fix) {
  124. case FBMON_FIX_HEADER:
  125. printk("fbmon: trying a header reconstruct\n");
  126. memcpy(edid, edid_v1_header, 8);
  127. break;
  128. case FBMON_FIX_INPUT:
  129. printk("fbmon: trying to fix input type\n");
  130. b = edid + EDID_STRUCT_DISPLAY;
  131. b[0] &= ~0x80;
  132. edid[127] += 0x80;
  133. }
  134. }
  135. static int edid_checksum(unsigned char *edid)
  136. {
  137. unsigned char i, csum = 0, all_null = 0;
  138. int err = 0, fix = check_edid(edid);
  139. if (fix)
  140. fix_edid(edid, fix);
  141. for (i = 0; i < EDID_LENGTH; i++) {
  142. csum += edid[i];
  143. all_null |= edid[i];
  144. }
  145. if (csum == 0x00 && all_null) {
  146. /* checksum passed, everything's good */
  147. err = 1;
  148. }
  149. return err;
  150. }
  151. static int edid_check_header(unsigned char *edid)
  152. {
  153. int i, err = 1, fix = check_edid(edid);
  154. if (fix)
  155. fix_edid(edid, fix);
  156. for (i = 0; i < 8; i++) {
  157. if (edid[i] != edid_v1_header[i])
  158. err = 0;
  159. }
  160. return err;
  161. }
  162. static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs)
  163. {
  164. specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
  165. specs->manufacturer[1] = ((block[0] & 0x03) << 3) +
  166. ((block[1] & 0xe0) >> 5) + '@';
  167. specs->manufacturer[2] = (block[1] & 0x1f) + '@';
  168. specs->manufacturer[3] = 0;
  169. specs->model = block[2] + (block[3] << 8);
  170. specs->serial = block[4] + (block[5] << 8) +
  171. (block[6] << 16) + (block[7] << 24);
  172. specs->year = block[9] + 1990;
  173. specs->week = block[8];
  174. DPRINTK(" Manufacturer: %s\n", specs->manufacturer);
  175. DPRINTK(" Model: %x\n", specs->model);
  176. DPRINTK(" Serial#: %u\n", specs->serial);
  177. DPRINTK(" Year: %u Week %u\n", specs->year, specs->week);
  178. }
  179. static void get_dpms_capabilities(unsigned char flags,
  180. struct fb_monspecs *specs)
  181. {
  182. specs->dpms = 0;
  183. if (flags & DPMS_ACTIVE_OFF)
  184. specs->dpms |= FB_DPMS_ACTIVE_OFF;
  185. if (flags & DPMS_SUSPEND)
  186. specs->dpms |= FB_DPMS_SUSPEND;
  187. if (flags & DPMS_STANDBY)
  188. specs->dpms |= FB_DPMS_STANDBY;
  189. DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n",
  190. (flags & DPMS_ACTIVE_OFF) ? "yes" : "no",
  191. (flags & DPMS_SUSPEND) ? "yes" : "no",
  192. (flags & DPMS_STANDBY) ? "yes" : "no");
  193. }
  194. static void get_chroma(unsigned char *block, struct fb_monspecs *specs)
  195. {
  196. int tmp;
  197. DPRINTK(" Chroma\n");
  198. /* Chromaticity data */
  199. tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2);
  200. tmp *= 1000;
  201. tmp += 512;
  202. specs->chroma.redx = tmp/1024;
  203. DPRINTK(" RedX: 0.%03d ", specs->chroma.redx);
  204. tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2);
  205. tmp *= 1000;
  206. tmp += 512;
  207. specs->chroma.redy = tmp/1024;
  208. DPRINTK("RedY: 0.%03d\n", specs->chroma.redy);
  209. tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2);
  210. tmp *= 1000;
  211. tmp += 512;
  212. specs->chroma.greenx = tmp/1024;
  213. DPRINTK(" GreenX: 0.%03d ", specs->chroma.greenx);
  214. tmp = (block[5] & 3) | (block[0xa] << 2);
  215. tmp *= 1000;
  216. tmp += 512;
  217. specs->chroma.greeny = tmp/1024;
  218. DPRINTK("GreenY: 0.%03d\n", specs->chroma.greeny);
  219. tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2);
  220. tmp *= 1000;
  221. tmp += 512;
  222. specs->chroma.bluex = tmp/1024;
  223. DPRINTK(" BlueX: 0.%03d ", specs->chroma.bluex);
  224. tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2);
  225. tmp *= 1000;
  226. tmp += 512;
  227. specs->chroma.bluey = tmp/1024;
  228. DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey);
  229. tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2);
  230. tmp *= 1000;
  231. tmp += 512;
  232. specs->chroma.whitex = tmp/1024;
  233. DPRINTK(" WhiteX: 0.%03d ", specs->chroma.whitex);
  234. tmp = (block[6] & 3) | (block[0xe] << 2);
  235. tmp *= 1000;
  236. tmp += 512;
  237. specs->chroma.whitey = tmp/1024;
  238. DPRINTK("WhiteY: 0.%03d\n", specs->chroma.whitey);
  239. }
  240. static int edid_is_serial_block(unsigned char *block)
  241. {
  242. if ((block[0] == 0x00) && (block[1] == 0x00) &&
  243. (block[2] == 0x00) && (block[3] == 0xff) &&
  244. (block[4] == 0x00))
  245. return 1;
  246. else
  247. return 0;
  248. }
  249. static int edid_is_ascii_block(unsigned char *block)
  250. {
  251. if ((block[0] == 0x00) && (block[1] == 0x00) &&
  252. (block[2] == 0x00) && (block[3] == 0xfe) &&
  253. (block[4] == 0x00))
  254. return 1;
  255. else
  256. return 0;
  257. }
  258. static int edid_is_limits_block(unsigned char *block)
  259. {
  260. if ((block[0] == 0x00) && (block[1] == 0x00) &&
  261. (block[2] == 0x00) && (block[3] == 0xfd) &&
  262. (block[4] == 0x00))
  263. return 1;
  264. else
  265. return 0;
  266. }
  267. static int edid_is_monitor_block(unsigned char *block)
  268. {
  269. if ((block[0] == 0x00) && (block[1] == 0x00) &&
  270. (block[2] == 0x00) && (block[3] == 0xfc) &&
  271. (block[4] == 0x00))
  272. return 1;
  273. else
  274. return 0;
  275. }
  276. static void calc_mode_timings(int xres, int yres, int refresh, struct fb_videomode *mode)
  277. {
  278. struct fb_var_screeninfo var;
  279. struct fb_info info;
  280. var.xres = xres;
  281. var.yres = yres;
  282. fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
  283. refresh, &var, &info);
  284. mode->xres = xres;
  285. mode->yres = yres;
  286. mode->pixclock = var.pixclock;
  287. mode->refresh = refresh;
  288. mode->left_margin = var.left_margin;
  289. mode->right_margin = var.right_margin;
  290. mode->upper_margin = var.upper_margin;
  291. mode->lower_margin = var.lower_margin;
  292. mode->hsync_len = var.hsync_len;
  293. mode->vsync_len = var.vsync_len;
  294. mode->vmode = 0;
  295. mode->sync = 0;
  296. }
  297. static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
  298. {
  299. int num = 0;
  300. unsigned char c;
  301. c = block[0];
  302. if (c&0x80) {
  303. calc_mode_timings(720, 400, 70, &mode[num]);
  304. mode[num++].flag = FB_MODE_IS_CALCULATED;
  305. DPRINTK(" 720x400@70Hz\n");
  306. }
  307. if (c&0x40) {
  308. calc_mode_timings(720, 400, 88, &mode[num]);
  309. mode[num++].flag = FB_MODE_IS_CALCULATED;
  310. DPRINTK(" 720x400@88Hz\n");
  311. }
  312. if (c&0x20) {
  313. mode[num++] = vesa_modes[3];
  314. DPRINTK(" 640x480@60Hz\n");
  315. }
  316. if (c&0x10) {
  317. calc_mode_timings(640, 480, 67, &mode[num]);
  318. mode[num++].flag = FB_MODE_IS_CALCULATED;
  319. DPRINTK(" 640x480@67Hz\n");
  320. }
  321. if (c&0x08) {
  322. mode[num++] = vesa_modes[4];
  323. DPRINTK(" 640x480@72Hz\n");
  324. }
  325. if (c&0x04) {
  326. mode[num++] = vesa_modes[5];
  327. DPRINTK(" 640x480@75Hz\n");
  328. }
  329. if (c&0x02) {
  330. mode[num++] = vesa_modes[7];
  331. DPRINTK(" 800x600@56Hz\n");
  332. }
  333. if (c&0x01) {
  334. mode[num++] = vesa_modes[8];
  335. DPRINTK(" 800x600@60Hz\n");
  336. }
  337. c = block[1];
  338. if (c&0x80) {
  339. mode[num++] = vesa_modes[9];
  340. DPRINTK(" 800x600@72Hz\n");
  341. }
  342. if (c&0x40) {
  343. mode[num++] = vesa_modes[10];
  344. DPRINTK(" 800x600@75Hz\n");
  345. }
  346. if (c&0x20) {
  347. calc_mode_timings(832, 624, 75, &mode[num]);
  348. mode[num++].flag = FB_MODE_IS_CALCULATED;
  349. DPRINTK(" 832x624@75Hz\n");
  350. }
  351. if (c&0x10) {
  352. mode[num++] = vesa_modes[12];
  353. DPRINTK(" 1024x768@87Hz Interlaced\n");
  354. }
  355. if (c&0x08) {
  356. mode[num++] = vesa_modes[13];
  357. DPRINTK(" 1024x768@60Hz\n");
  358. }
  359. if (c&0x04) {
  360. mode[num++] = vesa_modes[14];
  361. DPRINTK(" 1024x768@70Hz\n");
  362. }
  363. if (c&0x02) {
  364. mode[num++] = vesa_modes[15];
  365. DPRINTK(" 1024x768@75Hz\n");
  366. }
  367. if (c&0x01) {
  368. mode[num++] = vesa_modes[21];
  369. DPRINTK(" 1280x1024@75Hz\n");
  370. }
  371. c = block[2];
  372. if (c&0x80) {
  373. mode[num++] = vesa_modes[17];
  374. DPRINTK(" 1152x870@75Hz\n");
  375. }
  376. DPRINTK(" Manufacturer's mask: %x\n",c&0x7F);
  377. return num;
  378. }
  379. static int get_std_timing(unsigned char *block, struct fb_videomode *mode)
  380. {
  381. int xres, yres = 0, refresh, ratio, i;
  382. xres = (block[0] + 31) * 8;
  383. if (xres <= 256)
  384. return 0;
  385. ratio = (block[1] & 0xc0) >> 6;
  386. switch (ratio) {
  387. case 0:
  388. yres = xres;
  389. break;
  390. case 1:
  391. yres = (xres * 3)/4;
  392. break;
  393. case 2:
  394. yres = (xres * 4)/5;
  395. break;
  396. case 3:
  397. yres = (xres * 9)/16;
  398. break;
  399. }
  400. refresh = (block[1] & 0x3f) + 60;
  401. DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh);
  402. for (i = 0; i < VESA_MODEDB_SIZE; i++) {
  403. if (vesa_modes[i].xres == xres &&
  404. vesa_modes[i].yres == yres &&
  405. vesa_modes[i].refresh == refresh) {
  406. *mode = vesa_modes[i];
  407. mode->flag |= FB_MODE_IS_STANDARD;
  408. return 1;
  409. }
  410. }
  411. calc_mode_timings(xres, yres, refresh, mode);
  412. return 1;
  413. }
  414. static int get_dst_timing(unsigned char *block,
  415. struct fb_videomode *mode)
  416. {
  417. int j, num = 0;
  418. for (j = 0; j < 6; j++, block+= STD_TIMING_DESCRIPTION_SIZE)
  419. num += get_std_timing(block, &mode[num]);
  420. return num;
  421. }
  422. static void get_detailed_timing(unsigned char *block,
  423. struct fb_videomode *mode)
  424. {
  425. mode->xres = H_ACTIVE;
  426. mode->yres = V_ACTIVE;
  427. mode->pixclock = PIXEL_CLOCK;
  428. mode->pixclock /= 1000;
  429. mode->pixclock = KHZ2PICOS(mode->pixclock);
  430. mode->right_margin = H_SYNC_OFFSET;
  431. mode->left_margin = (H_ACTIVE + H_BLANKING) -
  432. (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
  433. mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
  434. V_SYNC_WIDTH;
  435. mode->lower_margin = V_SYNC_OFFSET;
  436. mode->hsync_len = H_SYNC_WIDTH;
  437. mode->vsync_len = V_SYNC_WIDTH;
  438. if (HSYNC_POSITIVE)
  439. mode->sync |= FB_SYNC_HOR_HIGH_ACT;
  440. if (VSYNC_POSITIVE)
  441. mode->sync |= FB_SYNC_VERT_HIGH_ACT;
  442. mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
  443. (V_ACTIVE + V_BLANKING));
  444. mode->vmode = 0;
  445. mode->flag = FB_MODE_IS_DETAILED;
  446. DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000);
  447. DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
  448. H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
  449. DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
  450. V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
  451. DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
  452. (VSYNC_POSITIVE) ? "+" : "-");
  453. }
  454. /**
  455. * fb_create_modedb - create video mode database
  456. * @edid: EDID data
  457. * @dbsize: database size
  458. *
  459. * RETURNS: struct fb_videomode, @dbsize contains length of database
  460. *
  461. * DESCRIPTION:
  462. * This function builds a mode database using the contents of the EDID
  463. * data
  464. */
  465. static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
  466. {
  467. struct fb_videomode *mode, *m;
  468. unsigned char *block;
  469. int num = 0, i;
  470. mode = kmalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
  471. if (mode == NULL)
  472. return NULL;
  473. memset(mode, 0, 50 * sizeof(struct fb_videomode));
  474. if (edid == NULL || !edid_checksum(edid) ||
  475. !edid_check_header(edid)) {
  476. kfree(mode);
  477. return NULL;
  478. }
  479. *dbsize = 0;
  480. DPRINTK(" Supported VESA Modes\n");
  481. block = edid + ESTABLISHED_TIMING_1;
  482. num += get_est_timing(block, &mode[num]);
  483. DPRINTK(" Standard Timings\n");
  484. block = edid + STD_TIMING_DESCRIPTIONS_START;
  485. for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
  486. num += get_std_timing(block, &mode[num]);
  487. DPRINTK(" Detailed Timings\n");
  488. block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
  489. for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
  490. int first = 1;
  491. if (block[0] == 0x00 && block[1] == 0x00) {
  492. if (block[3] == 0xfa) {
  493. num += get_dst_timing(block + 5, &mode[num]);
  494. }
  495. } else {
  496. get_detailed_timing(block, &mode[num]);
  497. if (first) {
  498. mode[num].flag |= FB_MODE_IS_FIRST;
  499. first = 0;
  500. }
  501. num++;
  502. }
  503. }
  504. /* Yikes, EDID data is totally useless */
  505. if (!num) {
  506. kfree(mode);
  507. return NULL;
  508. }
  509. *dbsize = num;
  510. m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL);
  511. if (!m)
  512. return mode;
  513. memmove(m, mode, num * sizeof(struct fb_videomode));
  514. kfree(mode);
  515. return m;
  516. }
  517. /**
  518. * fb_destroy_modedb - destroys mode database
  519. * @modedb: mode database to destroy
  520. *
  521. * DESCRIPTION:
  522. * Destroy mode database created by fb_create_modedb
  523. */
  524. void fb_destroy_modedb(struct fb_videomode *modedb)
  525. {
  526. kfree(modedb);
  527. }
  528. static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
  529. {
  530. int i, retval = 1;
  531. unsigned char *block;
  532. block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
  533. DPRINTK(" Monitor Operating Limits: ");
  534. for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
  535. if (edid_is_limits_block(block)) {
  536. specs->hfmin = H_MIN_RATE * 1000;
  537. specs->hfmax = H_MAX_RATE * 1000;
  538. specs->vfmin = V_MIN_RATE;
  539. specs->vfmax = V_MAX_RATE;
  540. specs->dclkmax = MAX_PIXEL_CLOCK * 1000000;
  541. specs->gtf = (GTF_SUPPORT) ? 1 : 0;
  542. retval = 0;
  543. DPRINTK("From EDID\n");
  544. break;
  545. }
  546. }
  547. /* estimate monitor limits based on modes supported */
  548. if (retval) {
  549. struct fb_videomode *modes;
  550. int num_modes, i, hz, hscan, pixclock;
  551. modes = fb_create_modedb(edid, &num_modes);
  552. if (!modes) {
  553. DPRINTK("None Available\n");
  554. return 1;
  555. }
  556. retval = 0;
  557. for (i = 0; i < num_modes; i++) {
  558. hz = modes[i].refresh;
  559. pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
  560. hscan = (modes[i].yres * 105 * hz + 5000)/100;
  561. if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
  562. specs->dclkmax = pixclock;
  563. if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
  564. specs->dclkmin = pixclock;
  565. if (specs->hfmax == 0 || specs->hfmax < hscan)
  566. specs->hfmax = hscan;
  567. if (specs->hfmin == 0 || specs->hfmin > hscan)
  568. specs->hfmin = hscan;
  569. if (specs->vfmax == 0 || specs->vfmax < hz)
  570. specs->vfmax = hz;
  571. if (specs->vfmin == 0 || specs->vfmin > hz)
  572. specs->vfmin = hz;
  573. }
  574. DPRINTK("Extrapolated\n");
  575. fb_destroy_modedb(modes);
  576. }
  577. DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n",
  578. specs->hfmin/1000, specs->hfmax/1000, specs->vfmin,
  579. specs->vfmax, specs->dclkmax/1000000);
  580. return retval;
  581. }
  582. static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs)
  583. {
  584. unsigned char c, *block;
  585. block = edid + EDID_STRUCT_DISPLAY;
  586. fb_get_monitor_limits(edid, specs);
  587. c = block[0] & 0x80;
  588. specs->input = 0;
  589. if (c) {
  590. specs->input |= FB_DISP_DDI;
  591. DPRINTK(" Digital Display Input");
  592. } else {
  593. DPRINTK(" Analog Display Input: Input Voltage - ");
  594. switch ((block[0] & 0x60) >> 5) {
  595. case 0:
  596. DPRINTK("0.700V/0.300V");
  597. specs->input |= FB_DISP_ANA_700_300;
  598. break;
  599. case 1:
  600. DPRINTK("0.714V/0.286V");
  601. specs->input |= FB_DISP_ANA_714_286;
  602. break;
  603. case 2:
  604. DPRINTK("1.000V/0.400V");
  605. specs->input |= FB_DISP_ANA_1000_400;
  606. break;
  607. case 3:
  608. DPRINTK("0.700V/0.000V");
  609. specs->input |= FB_DISP_ANA_700_000;
  610. break;
  611. }
  612. }
  613. DPRINTK("\n Sync: ");
  614. c = block[0] & 0x10;
  615. if (c)
  616. DPRINTK(" Configurable signal level\n");
  617. c = block[0] & 0x0f;
  618. specs->signal = 0;
  619. if (c & 0x10) {
  620. DPRINTK("Blank to Blank ");
  621. specs->signal |= FB_SIGNAL_BLANK_BLANK;
  622. }
  623. if (c & 0x08) {
  624. DPRINTK("Separate ");
  625. specs->signal |= FB_SIGNAL_SEPARATE;
  626. }
  627. if (c & 0x04) {
  628. DPRINTK("Composite ");
  629. specs->signal |= FB_SIGNAL_COMPOSITE;
  630. }
  631. if (c & 0x02) {
  632. DPRINTK("Sync on Green ");
  633. specs->signal |= FB_SIGNAL_SYNC_ON_GREEN;
  634. }
  635. if (c & 0x01) {
  636. DPRINTK("Serration on ");
  637. specs->signal |= FB_SIGNAL_SERRATION_ON;
  638. }
  639. DPRINTK("\n");
  640. specs->max_x = block[1];
  641. specs->max_y = block[2];
  642. DPRINTK(" Max H-size in cm: ");
  643. if (specs->max_x)
  644. DPRINTK("%d\n", specs->max_x);
  645. else
  646. DPRINTK("variable\n");
  647. DPRINTK(" Max V-size in cm: ");
  648. if (specs->max_y)
  649. DPRINTK("%d\n", specs->max_y);
  650. else
  651. DPRINTK("variable\n");
  652. c = block[3];
  653. specs->gamma = c+100;
  654. DPRINTK(" Gamma: ");
  655. DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100);
  656. get_dpms_capabilities(block[4], specs);
  657. switch ((block[4] & 0x18) >> 3) {
  658. case 0:
  659. DPRINTK(" Monochrome/Grayscale\n");
  660. specs->input |= FB_DISP_MONO;
  661. break;
  662. case 1:
  663. DPRINTK(" RGB Color Display\n");
  664. specs->input |= FB_DISP_RGB;
  665. break;
  666. case 2:
  667. DPRINTK(" Non-RGB Multicolor Display\n");
  668. specs->input |= FB_DISP_MULTI;
  669. break;
  670. default:
  671. DPRINTK(" Unknown\n");
  672. specs->input |= FB_DISP_UNKNOWN;
  673. break;
  674. }
  675. get_chroma(block, specs);
  676. specs->misc = 0;
  677. c = block[4] & 0x7;
  678. if (c & 0x04) {
  679. DPRINTK(" Default color format is primary\n");
  680. specs->misc |= FB_MISC_PRIM_COLOR;
  681. }
  682. if (c & 0x02) {
  683. DPRINTK(" First DETAILED Timing is preferred\n");
  684. specs->misc |= FB_MISC_1ST_DETAIL;
  685. }
  686. if (c & 0x01) {
  687. printk(" Display is GTF capable\n");
  688. specs->gtf = 1;
  689. }
  690. }
  691. static int edid_is_timing_block(unsigned char *block)
  692. {
  693. if ((block[0] != 0x00) || (block[1] != 0x00) ||
  694. (block[2] != 0x00) || (block[4] != 0x00))
  695. return 1;
  696. else
  697. return 0;
  698. }
  699. int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
  700. {
  701. int i;
  702. unsigned char *block;
  703. if (edid == NULL || var == NULL)
  704. return 1;
  705. if (!(edid_checksum(edid)))
  706. return 1;
  707. if (!(edid_check_header(edid)))
  708. return 1;
  709. block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
  710. for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
  711. if (edid_is_timing_block(block)) {
  712. var->xres = var->xres_virtual = H_ACTIVE;
  713. var->yres = var->yres_virtual = V_ACTIVE;
  714. var->height = var->width = -1;
  715. var->right_margin = H_SYNC_OFFSET;
  716. var->left_margin = (H_ACTIVE + H_BLANKING) -
  717. (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
  718. var->upper_margin = V_BLANKING - V_SYNC_OFFSET -
  719. V_SYNC_WIDTH;
  720. var->lower_margin = V_SYNC_OFFSET;
  721. var->hsync_len = H_SYNC_WIDTH;
  722. var->vsync_len = V_SYNC_WIDTH;
  723. var->pixclock = PIXEL_CLOCK;
  724. var->pixclock /= 1000;
  725. var->pixclock = KHZ2PICOS(var->pixclock);
  726. if (HSYNC_POSITIVE)
  727. var->sync |= FB_SYNC_HOR_HIGH_ACT;
  728. if (VSYNC_POSITIVE)
  729. var->sync |= FB_SYNC_VERT_HIGH_ACT;
  730. return 0;
  731. }
  732. }
  733. return 1;
  734. }
  735. void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
  736. {
  737. unsigned char *block;
  738. int i;
  739. if (edid == NULL)
  740. return;
  741. if (!(edid_checksum(edid)))
  742. return;
  743. if (!(edid_check_header(edid)))
  744. return;
  745. memset(specs, 0, sizeof(struct fb_monspecs));
  746. specs->version = edid[EDID_STRUCT_VERSION];
  747. specs->revision = edid[EDID_STRUCT_REVISION];
  748. DPRINTK("========================================\n");
  749. DPRINTK("Display Information (EDID)\n");
  750. DPRINTK("========================================\n");
  751. DPRINTK(" EDID Version %d.%d\n", (int) specs->version,
  752. (int) specs->revision);
  753. parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs);
  754. block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
  755. for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
  756. if (edid_is_serial_block(block)) {
  757. copy_string(block, specs->serial_no);
  758. DPRINTK(" Serial Number: %s\n", specs->serial_no);
  759. } else if (edid_is_ascii_block(block)) {
  760. copy_string(block, specs->ascii);
  761. DPRINTK(" ASCII Block: %s\n", specs->ascii);
  762. } else if (edid_is_monitor_block(block)) {
  763. copy_string(block, specs->monitor);
  764. DPRINTK(" Monitor Name: %s\n", specs->monitor);
  765. }
  766. }
  767. DPRINTK(" Display Characteristics:\n");
  768. get_monspecs(edid, specs);
  769. specs->modedb = fb_create_modedb(edid, &specs->modedb_len);
  770. DPRINTK("========================================\n");
  771. }
  772. /*
  773. * VESA Generalized Timing Formula (GTF)
  774. */
  775. #define FLYBACK 550
  776. #define V_FRONTPORCH 1
  777. #define H_OFFSET 40
  778. #define H_SCALEFACTOR 20
  779. #define H_BLANKSCALE 128
  780. #define H_GRADIENT 600
  781. #define C_VAL 30
  782. #define M_VAL 300
  783. struct __fb_timings {
  784. u32 dclk;
  785. u32 hfreq;
  786. u32 vfreq;
  787. u32 hactive;
  788. u32 vactive;
  789. u32 hblank;
  790. u32 vblank;
  791. u32 htotal;
  792. u32 vtotal;
  793. };
  794. /**
  795. * fb_get_vblank - get vertical blank time
  796. * @hfreq: horizontal freq
  797. *
  798. * DESCRIPTION:
  799. * vblank = right_margin + vsync_len + left_margin
  800. *
  801. * given: right_margin = 1 (V_FRONTPORCH)
  802. * vsync_len = 3
  803. * flyback = 550
  804. *
  805. * flyback * hfreq
  806. * left_margin = --------------- - vsync_len
  807. * 1000000
  808. */
  809. static u32 fb_get_vblank(u32 hfreq)
  810. {
  811. u32 vblank;
  812. vblank = (hfreq * FLYBACK)/1000;
  813. vblank = (vblank + 500)/1000;
  814. return (vblank + V_FRONTPORCH);
  815. }
  816. /**
  817. * fb_get_hblank_by_freq - get horizontal blank time given hfreq
  818. * @hfreq: horizontal freq
  819. * @xres: horizontal resolution in pixels
  820. *
  821. * DESCRIPTION:
  822. *
  823. * xres * duty_cycle
  824. * hblank = ------------------
  825. * 100 - duty_cycle
  826. *
  827. * duty cycle = percent of htotal assigned to inactive display
  828. * duty cycle = C - (M/Hfreq)
  829. *
  830. * where: C = ((offset - scale factor) * blank_scale)
  831. * -------------------------------------- + scale factor
  832. * 256
  833. * M = blank_scale * gradient
  834. *
  835. */
  836. static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
  837. {
  838. u32 c_val, m_val, duty_cycle, hblank;
  839. c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
  840. H_SCALEFACTOR) * 1000;
  841. m_val = (H_BLANKSCALE * H_GRADIENT)/256;
  842. m_val = (m_val * 1000000)/hfreq;
  843. duty_cycle = c_val - m_val;
  844. hblank = (xres * duty_cycle)/(100000 - duty_cycle);
  845. return (hblank);
  846. }
  847. /**
  848. * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock
  849. * @dclk: pixelclock in Hz
  850. * @xres: horizontal resolution in pixels
  851. *
  852. * DESCRIPTION:
  853. *
  854. * xres * duty_cycle
  855. * hblank = ------------------
  856. * 100 - duty_cycle
  857. *
  858. * duty cycle = percent of htotal assigned to inactive display
  859. * duty cycle = C - (M * h_period)
  860. *
  861. * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100
  862. * -----------------------------------------------
  863. * 2 * M
  864. * M = 300;
  865. * C = 30;
  866. */
  867. static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres)
  868. {
  869. u32 duty_cycle, h_period, hblank;
  870. dclk /= 1000;
  871. h_period = 100 - C_VAL;
  872. h_period *= h_period;
  873. h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk);
  874. h_period *=10000;
  875. h_period = int_sqrt(h_period);
  876. h_period -= (100 - C_VAL) * 100;
  877. h_period *= 1000;
  878. h_period /= 2 * M_VAL;
  879. duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100;
  880. hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8;
  881. hblank &= ~15;
  882. return (hblank);
  883. }
  884. /**
  885. * fb_get_hfreq - estimate hsync
  886. * @vfreq: vertical refresh rate
  887. * @yres: vertical resolution
  888. *
  889. * DESCRIPTION:
  890. *
  891. * (yres + front_port) * vfreq * 1000000
  892. * hfreq = -------------------------------------
  893. * (1000000 - (vfreq * FLYBACK)
  894. *
  895. */
  896. static u32 fb_get_hfreq(u32 vfreq, u32 yres)
  897. {
  898. u32 divisor, hfreq;
  899. divisor = (1000000 - (vfreq * FLYBACK))/1000;
  900. hfreq = (yres + V_FRONTPORCH) * vfreq * 1000;
  901. return (hfreq/divisor);
  902. }
  903. static void fb_timings_vfreq(struct __fb_timings *timings)
  904. {
  905. timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive);
  906. timings->vblank = fb_get_vblank(timings->hfreq);
  907. timings->vtotal = timings->vactive + timings->vblank;
  908. timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
  909. timings->hactive);
  910. timings->htotal = timings->hactive + timings->hblank;
  911. timings->dclk = timings->htotal * timings->hfreq;
  912. }
  913. static void fb_timings_hfreq(struct __fb_timings *timings)
  914. {
  915. timings->vblank = fb_get_vblank(timings->hfreq);
  916. timings->vtotal = timings->vactive + timings->vblank;
  917. timings->vfreq = timings->hfreq/timings->vtotal;
  918. timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
  919. timings->hactive);
  920. timings->htotal = timings->hactive + timings->hblank;
  921. timings->dclk = timings->htotal * timings->hfreq;
  922. }
  923. static void fb_timings_dclk(struct __fb_timings *timings)
  924. {
  925. timings->hblank = fb_get_hblank_by_dclk(timings->dclk,
  926. timings->hactive);
  927. timings->htotal = timings->hactive + timings->hblank;
  928. timings->hfreq = timings->dclk/timings->htotal;
  929. timings->vblank = fb_get_vblank(timings->hfreq);
  930. timings->vtotal = timings->vactive + timings->vblank;
  931. timings->vfreq = timings->hfreq/timings->vtotal;
  932. }
  933. /*
  934. * fb_get_mode - calculates video mode using VESA GTF
  935. * @flags: if: 0 - maximize vertical refresh rate
  936. * 1 - vrefresh-driven calculation;
  937. * 2 - hscan-driven calculation;
  938. * 3 - pixelclock-driven calculation;
  939. * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock
  940. * @var: pointer to fb_var_screeninfo
  941. * @info: pointer to fb_info
  942. *
  943. * DESCRIPTION:
  944. * Calculates video mode based on monitor specs using VESA GTF.
  945. * The GTF is best for VESA GTF compliant monitors but is
  946. * specifically formulated to work for older monitors as well.
  947. *
  948. * If @flag==0, the function will attempt to maximize the
  949. * refresh rate. Otherwise, it will calculate timings based on
  950. * the flag and accompanying value.
  951. *
  952. * If FB_IGNOREMON bit is set in @flags, monitor specs will be
  953. * ignored and @var will be filled with the calculated timings.
  954. *
  955. * All calculations are based on the VESA GTF Spreadsheet
  956. * available at VESA's public ftp (http://www.vesa.org).
  957. *
  958. * NOTES:
  959. * The timings generated by the GTF will be different from VESA
  960. * DMT. It might be a good idea to keep a table of standard
  961. * VESA modes as well. The GTF may also not work for some displays,
  962. * such as, and especially, analog TV.
  963. *
  964. * REQUIRES:
  965. * A valid info->monspecs, otherwise 'safe numbers' will be used.
  966. */
  967. int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info)
  968. {
  969. struct __fb_timings timings;
  970. u32 interlace = 1, dscan = 1;
  971. u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax;
  972. /*
  973. * If monspecs are invalid, use values that are enough
  974. * for 640x480@60
  975. */
  976. if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
  977. !info->monspecs.dclkmax ||
  978. info->monspecs.hfmax < info->monspecs.hfmin ||
  979. info->monspecs.vfmax < info->monspecs.vfmin ||
  980. info->monspecs.dclkmax < info->monspecs.dclkmin) {
  981. hfmin = 29000; hfmax = 30000;
  982. vfmin = 60; vfmax = 60;
  983. dclkmin = 0; dclkmax = 25000000;
  984. } else {
  985. hfmin = info->monspecs.hfmin;
  986. hfmax = info->monspecs.hfmax;
  987. vfmin = info->monspecs.vfmin;
  988. vfmax = info->monspecs.vfmax;
  989. dclkmin = info->monspecs.dclkmin;
  990. dclkmax = info->monspecs.dclkmax;
  991. }
  992. memset(&timings, 0, sizeof(struct __fb_timings));
  993. timings.hactive = var->xres;
  994. timings.vactive = var->yres;
  995. if (var->vmode & FB_VMODE_INTERLACED) {
  996. timings.vactive /= 2;
  997. interlace = 2;
  998. }
  999. if (var->vmode & FB_VMODE_DOUBLE) {
  1000. timings.vactive *= 2;
  1001. dscan = 2;
  1002. }
  1003. switch (flags & ~FB_IGNOREMON) {
  1004. case FB_MAXTIMINGS: /* maximize refresh rate */
  1005. timings.hfreq = hfmax;
  1006. fb_timings_hfreq(&timings);
  1007. if (timings.vfreq > vfmax) {
  1008. timings.vfreq = vfmax;
  1009. fb_timings_vfreq(&timings);
  1010. }
  1011. if (timings.dclk > dclkmax) {
  1012. timings.dclk = dclkmax;
  1013. fb_timings_dclk(&timings);
  1014. }
  1015. break;
  1016. case FB_VSYNCTIMINGS: /* vrefresh driven */
  1017. timings.vfreq = val;
  1018. fb_timings_vfreq(&timings);
  1019. break;
  1020. case FB_HSYNCTIMINGS: /* hsync driven */
  1021. timings.hfreq = val;
  1022. fb_timings_hfreq(&timings);
  1023. break;
  1024. case FB_DCLKTIMINGS: /* pixelclock driven */
  1025. timings.dclk = PICOS2KHZ(val) * 1000;
  1026. fb_timings_dclk(&timings);
  1027. break;
  1028. default:
  1029. return -EINVAL;
  1030. }
  1031. if (!(flags & FB_IGNOREMON) &&
  1032. (timings.vfreq < vfmin || timings.vfreq > vfmax ||
  1033. timings.hfreq < hfmin || timings.hfreq > hfmax ||
  1034. timings.dclk < dclkmin || timings.dclk > dclkmax))
  1035. return -EINVAL;
  1036. var->pixclock = KHZ2PICOS(timings.dclk/1000);
  1037. var->hsync_len = (timings.htotal * 8)/100;
  1038. var->right_margin = (timings.hblank/2) - var->hsync_len;
  1039. var->left_margin = timings.hblank - var->right_margin - var->hsync_len;
  1040. var->vsync_len = (3 * interlace)/dscan;
  1041. var->lower_margin = (1 * interlace)/dscan;
  1042. var->upper_margin = (timings.vblank * interlace)/dscan -
  1043. (var->vsync_len + var->lower_margin);
  1044. return 0;
  1045. }
  1046. #else
  1047. int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
  1048. {
  1049. return 1;
  1050. }
  1051. void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
  1052. {
  1053. specs = NULL;
  1054. }
  1055. void fb_destroy_modedb(struct fb_videomode *modedb)
  1056. {
  1057. }
  1058. int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
  1059. struct fb_info *info)
  1060. {
  1061. return -EINVAL;
  1062. }
  1063. #endif /* CONFIG_FB_MODE_HELPERS */
  1064. /*
  1065. * fb_validate_mode - validates var against monitor capabilities
  1066. * @var: pointer to fb_var_screeninfo
  1067. * @info: pointer to fb_info
  1068. *
  1069. * DESCRIPTION:
  1070. * Validates video mode against monitor capabilities specified in
  1071. * info->monspecs.
  1072. *
  1073. * REQUIRES:
  1074. * A valid info->monspecs.
  1075. */
  1076. int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info)
  1077. {
  1078. u32 hfreq, vfreq, htotal, vtotal, pixclock;
  1079. u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax;
  1080. /*
  1081. * If monspecs are invalid, use values that are enough
  1082. * for 640x480@60
  1083. */
  1084. if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
  1085. !info->monspecs.dclkmax ||
  1086. info->monspecs.hfmax < info->monspecs.hfmin ||
  1087. info->monspecs.vfmax < info->monspecs.vfmin ||
  1088. info->monspecs.dclkmax < info->monspecs.dclkmin) {
  1089. hfmin = 29000; hfmax = 30000;
  1090. vfmin = 60; vfmax = 60;
  1091. dclkmin = 0; dclkmax = 25000000;
  1092. } else {
  1093. hfmin = info->monspecs.hfmin;
  1094. hfmax = info->monspecs.hfmax;
  1095. vfmin = info->monspecs.vfmin;
  1096. vfmax = info->monspecs.vfmax;
  1097. dclkmin = info->monspecs.dclkmin;
  1098. dclkmax = info->monspecs.dclkmax;
  1099. }
  1100. if (!var->pixclock)
  1101. return -EINVAL;
  1102. pixclock = PICOS2KHZ(var->pixclock) * 1000;
  1103. htotal = var->xres + var->right_margin + var->hsync_len +
  1104. var->left_margin;
  1105. vtotal = var->yres + var->lower_margin + var->vsync_len +
  1106. var->upper_margin;
  1107. if (var->vmode & FB_VMODE_INTERLACED)
  1108. vtotal /= 2;
  1109. if (var->vmode & FB_VMODE_DOUBLE)
  1110. vtotal *= 2;
  1111. hfreq = pixclock/htotal;
  1112. vfreq = hfreq/vtotal;
  1113. return (vfreq < vfmin || vfreq > vfmax ||
  1114. hfreq < hfmin || hfreq > hfmax ||
  1115. pixclock < dclkmin || pixclock > dclkmax) ?
  1116. -EINVAL : 0;
  1117. }
  1118. EXPORT_SYMBOL(fb_parse_edid);
  1119. EXPORT_SYMBOL(fb_edid_to_monspecs);
  1120. EXPORT_SYMBOL(fb_get_mode);
  1121. EXPORT_SYMBOL(fb_validate_mode);
  1122. EXPORT_SYMBOL(fb_destroy_modedb);