Commit 0969d3d5 authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Ben Hutchings

tgafb: fix mode setting with fbset

commit 62496658 upstream.

Mode setting in the TGA driver is broken for these reasons:

- info->fix.line_length is set just once in tgafb_init_fix function. If
  we change videomode, info->fix.line_length is not recalculated - so
  the video mode is changed but the screen is corrupted because of wrong
  info->fix.line_length.

- info->fix.smem_len is set in tgafb_init_fix to the size of the default
  video mode (640x480). If we set a higher resolution,
  info->fix.smem_len is smaller than the current screen size, preventing
  the userspace program from mapping the framebuffer.

This patch fixes it:

- info->fix.line_length initialization is moved to tgafb_set_par so that
  it is recalculated with each mode change.

- info->fix.smem_len is set to a fixed value representing the real
  amount of video ram (the values are taken from xfree86 driver).

- add a check to tgafb_check_var to prevent us from setting a videomode
  that doesn't fit into videoram.

- in tgafb_register, tgafb_init_fix is moved upwards, to be called
  before fb_find_mode (because fb_find_mode already needs the videoram
  size set in tgafb_init_fix).
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Cc: stable@vga.kernel.org
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 3965194a
...@@ -192,6 +192,8 @@ tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ...@@ -192,6 +192,8 @@ tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
if (var->xres_virtual != var->xres || var->yres_virtual != var->yres) if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
return -EINVAL; return -EINVAL;
if (var->xres * var->yres * (var->bits_per_pixel >> 3) > info->fix.smem_len)
return -EINVAL;
if (var->nonstd) if (var->nonstd)
return -EINVAL; return -EINVAL;
if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ) if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
...@@ -272,6 +274,7 @@ tgafb_set_par(struct fb_info *info) ...@@ -272,6 +274,7 @@ tgafb_set_par(struct fb_info *info)
par->yres = info->var.yres; par->yres = info->var.yres;
par->pll_freq = pll_freq = 1000000000 / info->var.pixclock; par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
par->bits_per_pixel = info->var.bits_per_pixel; par->bits_per_pixel = info->var.bits_per_pixel;
info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
tga_type = par->tga_type; tga_type = par->tga_type;
...@@ -1318,6 +1321,7 @@ tgafb_init_fix(struct fb_info *info) ...@@ -1318,6 +1321,7 @@ tgafb_init_fix(struct fb_info *info)
int tga_bus_tc = TGA_BUS_TC(par->dev); int tga_bus_tc = TGA_BUS_TC(par->dev);
u8 tga_type = par->tga_type; u8 tga_type = par->tga_type;
const char *tga_type_name = NULL; const char *tga_type_name = NULL;
unsigned memory_size;
switch (tga_type) { switch (tga_type) {
case TGA_TYPE_8PLANE: case TGA_TYPE_8PLANE:
...@@ -1325,21 +1329,25 @@ tgafb_init_fix(struct fb_info *info) ...@@ -1325,21 +1329,25 @@ tgafb_init_fix(struct fb_info *info)
tga_type_name = "Digital ZLXp-E1"; tga_type_name = "Digital ZLXp-E1";
if (tga_bus_tc) if (tga_bus_tc)
tga_type_name = "Digital ZLX-E1"; tga_type_name = "Digital ZLX-E1";
memory_size = 2097152;
break; break;
case TGA_TYPE_24PLANE: case TGA_TYPE_24PLANE:
if (tga_bus_pci) if (tga_bus_pci)
tga_type_name = "Digital ZLXp-E2"; tga_type_name = "Digital ZLXp-E2";
if (tga_bus_tc) if (tga_bus_tc)
tga_type_name = "Digital ZLX-E2"; tga_type_name = "Digital ZLX-E2";
memory_size = 8388608;
break; break;
case TGA_TYPE_24PLUSZ: case TGA_TYPE_24PLUSZ:
if (tga_bus_pci) if (tga_bus_pci)
tga_type_name = "Digital ZLXp-E3"; tga_type_name = "Digital ZLXp-E3";
if (tga_bus_tc) if (tga_bus_tc)
tga_type_name = "Digital ZLX-E3"; tga_type_name = "Digital ZLX-E3";
memory_size = 16777216;
break; break;
default: default:
tga_type_name = "Unknown"; tga_type_name = "Unknown";
memory_size = 16777216;
break; break;
} }
...@@ -1351,9 +1359,8 @@ tgafb_init_fix(struct fb_info *info) ...@@ -1351,9 +1359,8 @@ tgafb_init_fix(struct fb_info *info)
? FB_VISUAL_PSEUDOCOLOR ? FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_DIRECTCOLOR); : FB_VISUAL_DIRECTCOLOR);
info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
info->fix.smem_start = (size_t) par->tga_fb_base; info->fix.smem_start = (size_t) par->tga_fb_base;
info->fix.smem_len = info->fix.line_length * par->yres; info->fix.smem_len = memory_size;
info->fix.mmio_start = (size_t) par->tga_regs_base; info->fix.mmio_start = (size_t) par->tga_regs_base;
info->fix.mmio_len = 512; info->fix.mmio_len = 512;
...@@ -1478,6 +1485,9 @@ tgafb_register(struct device *dev) ...@@ -1478,6 +1485,9 @@ tgafb_register(struct device *dev)
modedb_tga = &modedb_tc; modedb_tga = &modedb_tc;
modedbsize_tga = 1; modedbsize_tga = 1;
} }
tgafb_init_fix(info);
ret = fb_find_mode(&info->var, info, ret = fb_find_mode(&info->var, info,
mode_option ? mode_option : mode_option_tga, mode_option ? mode_option : mode_option_tga,
modedb_tga, modedbsize_tga, NULL, modedb_tga, modedbsize_tga, NULL,
...@@ -1495,7 +1505,6 @@ tgafb_register(struct device *dev) ...@@ -1495,7 +1505,6 @@ tgafb_register(struct device *dev)
} }
tgafb_set_par(info); tgafb_set_par(info);
tgafb_init_fix(info);
if (register_framebuffer(info) < 0) { if (register_framebuffer(info) < 0) {
printk(KERN_ERR "tgafb: Could not register framebuffer\n"); printk(KERN_ERR "tgafb: Could not register framebuffer\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment