diff options
author | Krzysztof Helt <krzysztof.h1@wp.pl> | 2009-03-31 18:25:09 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-01 11:59:27 -0400 |
commit | 99a4584752bb41330342a427d014482525de7433 (patch) | |
tree | 5a705f234f5ace69c028578102919394dfc0bf26 /drivers/video/cirrusfb.c | |
parent | 1b48cb563d59e03dbf530174f30c0ed3b6fba513 (diff) |
cirrusfb: check_var improvements
Break cirrusfb_decode_var() function into two parts:
cirrusfb_check_pixclock() which can be called from the
cirrusfb_check_var() aand merge rest into the cirrusfb_set_par_foo().
This allows rejecting modes with too high pixclock before before any
change to hardware state (and a console is messed up).
Also, fix RGB field's lengths for 8bpp modes to correct ones so X11 works
with fbdev driver with cirrusfb.
Kill some redundant function calls or register loads.
Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/cirrusfb.c')
-rw-r--r-- | drivers/video/cirrusfb.c | 195 |
1 files changed, 74 insertions, 121 deletions
diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 12f45520e5d3..bab713b63a0c 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c | |||
@@ -432,6 +432,53 @@ static int cirrusfb_check_mclk(struct fb_info *info, long freq) | |||
432 | return 0; | 432 | return 0; |
433 | } | 433 | } |
434 | 434 | ||
435 | static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, | ||
436 | struct fb_info *info) | ||
437 | { | ||
438 | long freq; | ||
439 | long maxclock; | ||
440 | struct cirrusfb_info *cinfo = info->par; | ||
441 | unsigned maxclockidx = var->bits_per_pixel >> 3; | ||
442 | |||
443 | /* convert from ps to kHz */ | ||
444 | freq = PICOS2KHZ(var->pixclock); | ||
445 | |||
446 | dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); | ||
447 | |||
448 | maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; | ||
449 | cinfo->multiplexing = 0; | ||
450 | |||
451 | /* If the frequency is greater than we can support, we might be able | ||
452 | * to use multiplexing for the video mode */ | ||
453 | if (freq > maxclock) { | ||
454 | switch (cinfo->btype) { | ||
455 | case BT_ALPINE: | ||
456 | case BT_GD5480: | ||
457 | cinfo->multiplexing = 1; | ||
458 | break; | ||
459 | |||
460 | default: | ||
461 | dev_err(info->device, | ||
462 | "Frequency greater than maxclock (%ld kHz)\n", | ||
463 | maxclock); | ||
464 | return -EINVAL; | ||
465 | } | ||
466 | } | ||
467 | #if 0 | ||
468 | /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where | ||
469 | * the VCLK is double the pixel clock. */ | ||
470 | switch (var->bits_per_pixel) { | ||
471 | case 16: | ||
472 | case 32: | ||
473 | if (var->xres <= 800) | ||
474 | /* Xbh has this type of clock for 32-bit */ | ||
475 | freq /= 2; | ||
476 | break; | ||
477 | } | ||
478 | #endif | ||
479 | return 0; | ||
480 | } | ||
481 | |||
435 | static int cirrusfb_check_var(struct fb_var_screeninfo *var, | 482 | static int cirrusfb_check_var(struct fb_var_screeninfo *var, |
436 | struct fb_info *info) | 483 | struct fb_info *info) |
437 | { | 484 | { |
@@ -449,7 +496,7 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, | |||
449 | 496 | ||
450 | case 8: | 497 | case 8: |
451 | var->red.offset = 0; | 498 | var->red.offset = 0; |
452 | var->red.length = 6; | 499 | var->red.length = 8; |
453 | var->green = var->red; | 500 | var->green = var->red; |
454 | var->blue = var->red; | 501 | var->blue = var->red; |
455 | break; | 502 | break; |
@@ -513,7 +560,6 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, | |||
513 | return -EINVAL; | 560 | return -EINVAL; |
514 | } | 561 | } |
515 | 562 | ||
516 | |||
517 | if (var->xoffset < 0) | 563 | if (var->xoffset < 0) |
518 | var->xoffset = 0; | 564 | var->xoffset = 0; |
519 | if (var->yoffset < 0) | 565 | if (var->yoffset < 0) |
@@ -544,80 +590,9 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, | |||
544 | return -EINVAL; | 590 | return -EINVAL; |
545 | } | 591 | } |
546 | 592 | ||
547 | return 0; | 593 | if (cirrusfb_check_pixclock(var, info)) |
548 | } | 594 | return -EINVAL; |
549 | |||
550 | static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, | ||
551 | struct fb_info *info) | ||
552 | { | ||
553 | long freq; | ||
554 | long maxclock; | ||
555 | int maxclockidx = var->bits_per_pixel >> 3; | ||
556 | struct cirrusfb_info *cinfo = info->par; | ||
557 | |||
558 | switch (var->bits_per_pixel) { | ||
559 | case 1: | ||
560 | info->fix.line_length = var->xres_virtual / 8; | ||
561 | info->fix.visual = FB_VISUAL_MONO10; | ||
562 | break; | ||
563 | |||
564 | case 8: | ||
565 | info->fix.line_length = var->xres_virtual; | ||
566 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
567 | break; | ||
568 | |||
569 | case 16: | ||
570 | case 32: | ||
571 | info->fix.line_length = var->xres_virtual * maxclockidx; | ||
572 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
573 | break; | ||
574 | |||
575 | default: | ||
576 | dev_dbg(info->device, | ||
577 | "Unsupported bpp size: %d\n", var->bits_per_pixel); | ||
578 | assert(false); | ||
579 | /* should never occur */ | ||
580 | break; | ||
581 | } | ||
582 | |||
583 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
584 | |||
585 | /* convert from ps to kHz */ | ||
586 | freq = PICOS2KHZ(var->pixclock); | ||
587 | |||
588 | dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); | ||
589 | |||
590 | maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; | ||
591 | cinfo->multiplexing = 0; | ||
592 | |||
593 | /* If the frequency is greater than we can support, we might be able | ||
594 | * to use multiplexing for the video mode */ | ||
595 | if (freq > maxclock) { | ||
596 | switch (cinfo->btype) { | ||
597 | case BT_ALPINE: | ||
598 | case BT_GD5480: | ||
599 | cinfo->multiplexing = 1; | ||
600 | break; | ||
601 | 595 | ||
602 | default: | ||
603 | dev_err(info->device, | ||
604 | "Frequency greater than maxclock (%ld kHz)\n", | ||
605 | maxclock); | ||
606 | return -EINVAL; | ||
607 | } | ||
608 | } | ||
609 | #if 0 | ||
610 | /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where | ||
611 | * the VCLK is double the pixel clock. */ | ||
612 | switch (var->bits_per_pixel) { | ||
613 | case 16: | ||
614 | case 32: | ||
615 | if (var->xres <= 800) | ||
616 | /* Xbh has this type of clock for 32-bit */ | ||
617 | freq /= 2; | ||
618 | break; | ||
619 | } | ||
620 | #endif | ||
621 | return 0; | 596 | return 0; |
622 | } | 597 | } |
623 | 598 | ||
@@ -653,7 +628,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
653 | struct fb_var_screeninfo *var = &info->var; | 628 | struct fb_var_screeninfo *var = &info->var; |
654 | u8 __iomem *regbase = cinfo->regbase; | 629 | u8 __iomem *regbase = cinfo->regbase; |
655 | unsigned char tmp; | 630 | unsigned char tmp; |
656 | int err; | ||
657 | int pitch; | 631 | int pitch; |
658 | const struct cirrusfb_board_info_rec *bi; | 632 | const struct cirrusfb_board_info_rec *bi; |
659 | int hdispend, hsyncstart, hsyncend, htotal; | 633 | int hdispend, hsyncstart, hsyncend, htotal; |
@@ -664,16 +638,28 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
664 | 638 | ||
665 | dev_dbg(info->device, "Requested mode: %dx%dx%d\n", | 639 | dev_dbg(info->device, "Requested mode: %dx%dx%d\n", |
666 | var->xres, var->yres, var->bits_per_pixel); | 640 | var->xres, var->yres, var->bits_per_pixel); |
667 | dev_dbg(info->device, "pixclock: %d\n", var->pixclock); | ||
668 | 641 | ||
669 | init_vgachip(info); | 642 | switch (var->bits_per_pixel) { |
643 | case 1: | ||
644 | info->fix.line_length = var->xres_virtual / 8; | ||
645 | info->fix.visual = FB_VISUAL_MONO10; | ||
646 | break; | ||
670 | 647 | ||
671 | err = cirrusfb_decode_var(var, info); | 648 | case 8: |
672 | if (err) { | 649 | info->fix.line_length = var->xres_virtual; |
673 | /* should never happen */ | 650 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; |
674 | dev_dbg(info->device, "mode change aborted. invalid var.\n"); | 651 | break; |
675 | return -EINVAL; | 652 | |
653 | case 16: | ||
654 | case 32: | ||
655 | info->fix.line_length = var->xres_virtual * | ||
656 | var->bits_per_pixel >> 3; | ||
657 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
658 | break; | ||
676 | } | 659 | } |
660 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
661 | |||
662 | init_vgachip(info); | ||
677 | 663 | ||
678 | bi = &cirrusfb_board_info[cinfo->btype]; | 664 | bi = &cirrusfb_board_info[cinfo->btype]; |
679 | 665 | ||
@@ -873,9 +859,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
873 | * address wrap, no compat. */ | 859 | * address wrap, no compat. */ |
874 | vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3); | 860 | vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3); |
875 | 861 | ||
876 | /* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); | ||
877 | * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */ | ||
878 | |||
879 | /* don't know if it would hurt to also program this if no interlaced */ | 862 | /* don't know if it would hurt to also program this if no interlaced */ |
880 | /* mode is used, but I feel better this way.. :-) */ | 863 | /* mode is used, but I feel better this way.. :-) */ |
881 | if (var->vmode & FB_VMODE_INTERLACED) | 864 | if (var->vmode & FB_VMODE_INTERLACED) |
@@ -883,8 +866,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
883 | else | 866 | else |
884 | vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */ | 867 | vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */ |
885 | 868 | ||
886 | vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0); | ||
887 | |||
888 | /* adjust horizontal/vertical sync type (low/high) */ | 869 | /* adjust horizontal/vertical sync type (low/high) */ |
889 | /* enable display memory & CRTC I/O address for color mode */ | 870 | /* enable display memory & CRTC I/O address for color mode */ |
890 | tmp = 0x03; | 871 | tmp = 0x03; |
@@ -896,8 +877,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
896 | tmp |= 0xc; | 877 | tmp |= 0xc; |
897 | WGen(cinfo, VGA_MIS_W, tmp); | 878 | WGen(cinfo, VGA_MIS_W, tmp); |
898 | 879 | ||
899 | /* Screen A Preset Row-Scan register */ | ||
900 | vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0); | ||
901 | /* text cursor on and start line */ | 880 | /* text cursor on and start line */ |
902 | vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0); | 881 | vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0); |
903 | /* text cursor end line */ | 882 | /* text cursor end line */ |
@@ -1248,7 +1227,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
1248 | dev_dbg(info->device, "CRT1e: %d\n", tmp); | 1227 | dev_dbg(info->device, "CRT1e: %d\n", tmp); |
1249 | } | 1228 | } |
1250 | 1229 | ||
1251 | |||
1252 | /* pixel panning */ | 1230 | /* pixel panning */ |
1253 | vga_wattr(regbase, CL_AR33, 0); | 1231 | vga_wattr(regbase, CL_AR33, 0); |
1254 | 1232 | ||
@@ -1274,9 +1252,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
1274 | vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp); | 1252 | vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp); |
1275 | dev_dbg(info->device, "CL_SEQR1: %d\n", tmp); | 1253 | dev_dbg(info->device, "CL_SEQR1: %d\n", tmp); |
1276 | 1254 | ||
1277 | /* pan to requested offset */ | ||
1278 | cirrusfb_pan_display(var, info); | ||
1279 | |||
1280 | #ifdef CIRRUSFB_DEBUG | 1255 | #ifdef CIRRUSFB_DEBUG |
1281 | cirrusfb_dbg_reg_dump(info, NULL); | 1256 | cirrusfb_dbg_reg_dump(info, NULL); |
1282 | #endif | 1257 | #endif |
@@ -1332,8 +1307,7 @@ static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green, | |||
1332 | static int cirrusfb_pan_display(struct fb_var_screeninfo *var, | 1307 | static int cirrusfb_pan_display(struct fb_var_screeninfo *var, |
1333 | struct fb_info *info) | 1308 | struct fb_info *info) |
1334 | { | 1309 | { |
1335 | int xoffset = 0; | 1310 | int xoffset; |
1336 | int yoffset = 0; | ||
1337 | unsigned long base; | 1311 | unsigned long base; |
1338 | unsigned char tmp, xpix; | 1312 | unsigned char tmp, xpix; |
1339 | struct cirrusfb_info *cinfo = info->par; | 1313 | struct cirrusfb_info *cinfo = info->par; |
@@ -1347,9 +1321,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, | |||
1347 | return -EINVAL; | 1321 | return -EINVAL; |
1348 | 1322 | ||
1349 | xoffset = var->xoffset * info->var.bits_per_pixel / 8; | 1323 | xoffset = var->xoffset * info->var.bits_per_pixel / 8; |
1350 | yoffset = var->yoffset; | ||
1351 | 1324 | ||
1352 | base = yoffset * info->fix.line_length + xoffset; | 1325 | base = var->yoffset * info->fix.line_length + xoffset; |
1353 | 1326 | ||
1354 | if (info->var.bits_per_pixel == 1) { | 1327 | if (info->var.bits_per_pixel == 1) { |
1355 | /* base is already correct */ | 1328 | /* base is already correct */ |
@@ -1363,10 +1336,8 @@ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, | |||
1363 | cirrusfb_WaitBLT(cinfo->regbase); | 1336 | cirrusfb_WaitBLT(cinfo->regbase); |
1364 | 1337 | ||
1365 | /* lower 8 + 8 bits of screen start address */ | 1338 | /* lower 8 + 8 bits of screen start address */ |
1366 | vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, | 1339 | vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff); |
1367 | (unsigned char) (base & 0xff)); | 1340 | vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff); |
1368 | vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, | ||
1369 | (unsigned char) (base >> 8)); | ||
1370 | 1341 | ||
1371 | /* 0xf2 is %11110010, exclude tmp bits */ | 1342 | /* 0xf2 is %11110010, exclude tmp bits */ |
1372 | tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2; | 1343 | tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2; |
@@ -1544,10 +1515,6 @@ static void init_vgachip(struct fb_info *info) | |||
1544 | 1515 | ||
1545 | /* FullBandwidth (video off) and 8/9 dot clock */ | 1516 | /* FullBandwidth (video off) and 8/9 dot clock */ |
1546 | vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21); | 1517 | vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21); |
1547 | /* polarity (-/-), disable access to display memory, | ||
1548 | * VGA_CRTC_START_HI base address: color | ||
1549 | */ | ||
1550 | WGen(cinfo, VGA_MIS_W, 0xc1); | ||
1551 | 1518 | ||
1552 | /* "magic cookie" - doesn't make any sense to me.. */ | 1519 | /* "magic cookie" - doesn't make any sense to me.. */ |
1553 | /* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */ | 1520 | /* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */ |
@@ -1614,10 +1581,6 @@ static void init_vgachip(struct fb_info *info) | |||
1614 | vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20); | 1581 | vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20); |
1615 | /* Text cursor end: - */ | 1582 | /* Text cursor end: - */ |
1616 | vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00); | 1583 | vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00); |
1617 | /* Screen start address high: 0 */ | ||
1618 | vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00); | ||
1619 | /* Screen start address low: 0 */ | ||
1620 | vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00); | ||
1621 | /* text cursor location high: 0 */ | 1584 | /* text cursor location high: 0 */ |
1622 | vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00); | 1585 | vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00); |
1623 | /* text cursor location low: 0 */ | 1586 | /* text cursor location low: 0 */ |
@@ -1625,10 +1588,6 @@ static void init_vgachip(struct fb_info *info) | |||
1625 | 1588 | ||
1626 | /* Underline Row scanline: - */ | 1589 | /* Underline Row scanline: - */ |
1627 | vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00); | 1590 | vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00); |
1628 | /* mode control: timing enable, byte mode, no compat modes */ | ||
1629 | vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3); | ||
1630 | /* Line Compare: not needed */ | ||
1631 | vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00); | ||
1632 | /* ### add 0x40 for text modes with > 30 MHz pixclock */ | 1591 | /* ### add 0x40 for text modes with > 30 MHz pixclock */ |
1633 | /* ext. display controls: ext.adr. wrap */ | 1592 | /* ext. display controls: ext.adr. wrap */ |
1634 | vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02); | 1593 | vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02); |
@@ -1697,12 +1656,6 @@ static void init_vgachip(struct fb_info *info) | |||
1697 | 1656 | ||
1698 | WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */ | 1657 | WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */ |
1699 | 1658 | ||
1700 | if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480) | ||
1701 | /* polarity (-/-), enable display mem, | ||
1702 | * VGA_CRTC_START_HI i/o base = color | ||
1703 | */ | ||
1704 | WGen(cinfo, VGA_MIS_W, 0xc3); | ||
1705 | |||
1706 | /* BLT Start/status: Blitter reset */ | 1659 | /* BLT Start/status: Blitter reset */ |
1707 | vga_wgfx(cinfo->regbase, CL_GR31, 0x04); | 1660 | vga_wgfx(cinfo->regbase, CL_GR31, 0x04); |
1708 | /* - " - : "end-of-reset" */ | 1661 | /* - " - : "end-of-reset" */ |
@@ -2052,7 +2005,7 @@ static int __devinit cirrusfb_register(struct fb_info *info) | |||
2052 | 2005 | ||
2053 | info->var.activate = FB_ACTIVATE_NOW; | 2006 | info->var.activate = FB_ACTIVATE_NOW; |
2054 | 2007 | ||
2055 | err = cirrusfb_decode_var(&info->var, info); | 2008 | err = cirrusfb_check_var(&info->var, info); |
2056 | if (err < 0) { | 2009 | if (err < 0) { |
2057 | /* should never happen */ | 2010 | /* should never happen */ |
2058 | dev_dbg(info->device, | 2011 | dev_dbg(info->device, |