From: James Simmons This fixes the bugs that where in mode switch via stty. The problem was we couldn't set the mode just by using the x and y resolution. We use modedb to fill in the rest. There also was a bug that allowed you to change the console resolution for drivers with fixed resolutions. This would mess up your display. Now that is fixed. --- 25-akpm/drivers/char/vt.c | 12 - 25-akpm/drivers/video/aty/atyfb_base.c | 5 25-akpm/drivers/video/aty/radeon_base.c | 1 25-akpm/drivers/video/console/fbcon.c | 284 +++++++++++++------------------- 25-akpm/drivers/video/console/fbcon.h | 1 25-akpm/drivers/video/modedb.c | 15 - 25-akpm/drivers/video/radeonfb.c | 1 25-akpm/include/linux/fb.h | 1 8 files changed, 135 insertions(+), 185 deletions(-) diff -puN drivers/char/vt.c~fbdev-mode-switching-fix drivers/char/vt.c --- 25/drivers/char/vt.c~fbdev-mode-switching-fix Wed May 19 15:02:08 2004 +++ 25-akpm/drivers/char/vt.c Wed May 19 15:02:09 2004 @@ -776,17 +776,17 @@ int vc_resize(int currcons, unsigned int old_row_size = video_size_row; old_screen_size = screenbuf_size; - video_num_lines = new_rows; - video_num_columns = new_cols; - video_size_row = new_row_size; - screenbuf_size = new_screen_size; - err = resize_screen(currcons, new_cols, new_rows); if (err) { kfree(newscreen); return err; } + video_num_lines = new_rows; + video_num_columns = new_cols; + video_size_row = new_row_size; + screenbuf_size = new_screen_size; + rlth = min(old_row_size, new_row_size); rrem = new_row_size - rlth; old_origin = origin; @@ -811,8 +811,6 @@ int vc_resize(int currcons, unsigned int screenbuf = newscreen; kmalloced = 1; screenbuf_size = new_screen_size; - if (IS_VISIBLE) - err = resize_screen(currcons, new_cols, new_rows); set_origin(currcons); /* do part of a reset_terminal() */ diff -puN drivers/video/aty/atyfb_base.c~fbdev-mode-switching-fix drivers/video/aty/atyfb_base.c --- 25/drivers/video/aty/atyfb_base.c~fbdev-mode-switching-fix Wed May 19 15:02:08 2004 +++ 25-akpm/drivers/video/aty/atyfb_base.c Wed May 19 15:02:09 2004 @@ -1231,10 +1231,7 @@ static void atyfb_palette(int enter) for (i = 0; i < FB_MAX; i++) { info = registered_fb[i]; - if (info && - info->fbops == &atyfb_ops && - info->display_fg && - info->display_fg->vc_num == i) { + if (info && info->fbops == &atyfb_ops) { par = (struct atyfb_par *) info->par; atyfb_save_palette(par, enter); diff -puN drivers/video/aty/radeon_base.c~fbdev-mode-switching-fix drivers/video/aty/radeon_base.c --- 25/drivers/video/aty/radeon_base.c~fbdev-mode-switching-fix Wed May 19 15:02:08 2004 +++ 25-akpm/drivers/video/aty/radeon_base.c Wed May 19 15:02:09 2004 @@ -1783,7 +1783,6 @@ static int __devinit radeon_set_fbinfo ( info->pseudo_palette = rinfo->pseudo_palette; info->flags = FBINFO_FLAG_DEFAULT; info->fbops = &radeonfb_ops; - info->display_fg = NULL; info->screen_base = (char *)rinfo->fb_base; /* Fill fix common fields */ diff -puN drivers/video/console/fbcon.c~fbdev-mode-switching-fix drivers/video/console/fbcon.c --- 25/drivers/video/console/fbcon.c~fbdev-mode-switching-fix Wed May 19 15:02:08 2004 +++ 25-akpm/drivers/video/console/fbcon.c Wed May 19 15:02:09 2004 @@ -115,6 +115,8 @@ static int softback_lines; static int first_fb_vc; static int last_fb_vc = MAX_NR_CONSOLES - 1; static int fbcon_is_default = 1; +/* font data */ +static char fontname[40]; #define REFCOUNT(fd) (((int *)(fd))[-1]) #define FNTSIZE(fd) (((int *)(fd))[-2]) @@ -168,9 +170,7 @@ static int fbcon_scrolldelta(struct vc_d /* * Internal routines */ -static void fbcon_set_display(struct vc_data *vc, int init, int logo); static __inline__ int real_y(struct display *p, int ypos); -static __inline__ void updatescrollmode(struct display *p, struct vc_data *vc); static __inline__ void ywrap_up(struct vc_data *vc, int count); static __inline__ void ywrap_down(struct vc_data *vc, int count); static __inline__ void ypan_up(struct vc_data *vc, int count); @@ -233,18 +233,15 @@ static void cursor_timer_handler(unsigne int __init fb_console_setup(char *this_opt) { - int unit, i, j; char *options; + int i, j; if (!this_opt || !*this_opt) return 0; while ((options = strsep(&this_opt, ",")) != NULL) { - if (!strncmp(options, "font:", 5)) { - for (unit = 0; unit < MAX_NR_CONSOLES; unit++) - strcpy(fb_display[unit].fontname, - options + 5); - } + if (!strncmp(options, "font:", 5)) + strcpy(fontname, options + 5); if (!strncmp(options, "scrollback:", 11)) { options += 11; @@ -442,11 +439,13 @@ void accel_clear_margins(struct vc_data static const char *fbcon_startup(void) { const char *display_desc = "frame buffer device"; + struct display *p = &fb_display[fg_console]; + struct vc_data *vc = vc_cons[fg_console].d; struct font_desc *font = NULL; struct module *owner; struct fb_info *info; - struct vc_data *vc; static int done = 0; + int cols, rows; int irqres; irqres = 1; @@ -493,37 +492,35 @@ static const char *fbcon_startup(void) softback_lines = 0; } - font = get_default_font(info->var.xres, info->var.yres); - - vc = (struct vc_data *) kmalloc(sizeof(struct vc_data), GFP_ATOMIC); - - if (!vc) { - if (softback_buf) - kfree((void *) softback_buf); - return NULL; - } - /* Setup default font */ - vc->vc_font.data = font->data; - vc->vc_font.width = font->width; - vc->vc_font.height = font->height; - vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ - - vc->vc_cols = info->var.xres/vc->vc_font.width; - vc->vc_rows = info->var.yres/vc->vc_font.height; + if (!p->fontdata) { + if (!fontname[0] || !(font = find_font(fontname))) + font = get_default_font(info->var.xres, + info->var.yres); + vc->vc_font.width = font->width; + vc->vc_font.height = font->height; + vc->vc_font.data = p->fontdata = font->data; + vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ + } - /* We trust the mode the driver supplies. */ + /* + * We must always set the mode. The mode of the previous console + * driver could be in the same resolution but we are using different + * hardware so we have to initialize the hardware. + */ if (info->fbops->fb_set_par) info->fbops->fb_set_par(info); + cols = info->var.xres/vc->vc_font.width; + rows = info->var.yres/vc->vc_font.height; + vc_resize(vc->vc_num, cols, rows); DPRINTK("mode: %s\n", info->fix.id); DPRINTK("visual: %d\n", info->fix.visual); DPRINTK("res: %dx%d-%d\n", info->var.xres, info->var.yres, info->var.bits_per_pixel); + con_set_default_unimap(vc->vc_num); - info->display_fg = vc; - #ifdef CONFIG_ATARI if (MACH_IS_ATARI) { cursor_blink_rate = ATARI_CURSOR_BLINK_RATE; @@ -598,99 +595,60 @@ static const char *fbcon_startup(void) static void fbcon_init(struct vc_data *vc, int init) { - int unit = vc->vc_num; - struct fb_info *info; - - /* on which frame buffer will we open this console? */ - info = registered_fb[(int) con2fb_map[unit]]; - - if (info->var.accel_flags) - fb_display[unit].scrollmode = SCROLL_YNOMOVE; - else - fb_display[unit].scrollmode = SCROLL_YREDRAW; - con_set_default_unimap(unit); - fbcon_set_display(vc, init, !init); -} - -static void fbcon_deinit(struct vc_data *vc) -{ - struct display *p = &fb_display[vc->vc_num]; - - fbcon_free_font(p); -} - -static __inline__ void updatescrollmode(struct display *p, struct vc_data *vc) -{ - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - - int m; - if (p->scrollmode & __SCROLL_YFIXED) - return; - if (divides(info->fix.ywrapstep, vc->vc_font.height) && - divides(vc->vc_font.height, info->var.yres_virtual)) - m = __SCROLL_YWRAP; - else if (divides(info->fix.ypanstep, vc->vc_font.height) && - info->var.yres_virtual >= info->var.yres + vc->vc_font.height) - m = __SCROLL_YPAN; - else if (p->scrollmode & __SCROLL_YNOMOVE) - m = __SCROLL_YREDRAW; - else - m = __SCROLL_YMOVE; - p->scrollmode = (p->scrollmode & ~__SCROLL_YMASK) | m; -} - -static void fbcon_set_display(struct vc_data *vc, int init, int logo) -{ struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - int nr_rows, nr_cols, old_rows, old_cols, i, charcnt = 256; - struct display *p = &fb_display[vc->vc_num]; + struct vc_data **default_mode = vc->vc_display_fg; + struct display *t, *p = &fb_display[vc->vc_num]; + int display_fg = (*default_mode)->vc_num; + int logo = 1, rows, cols, charcnt = 256; unsigned short *save = NULL, *r, *q; - struct font_desc *font; - if (vc->vc_num != fg_console || (info->flags & FBINFO_FLAG_MODULE) || + if (vc->vc_num != display_fg || (info->flags & FBINFO_FLAG_MODULE) || (info->fix.type == FB_TYPE_TEXT)) logo = 0; info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */ - for (i = 0; i < MAX_NR_CONSOLES; i++) - if (vc && i != vc->vc_num && fb_display[i].fontdata) - break; - - fbcon_free_font(p); - if (i < MAX_NR_CONSOLES) { - struct display *q = &fb_display[i]; - struct vc_data *tmp = vc_cons[i].d; - - /* If we are not the first console on this - fb, copy the font from that console */ - vc->vc_font.width = tmp->vc_font.width; - vc->vc_font.height = tmp->vc_font.height; - vc->vc_font.data = p->fontdata = q->fontdata; - p->userfont = q->userfont; - if (p->userfont) { - REFCOUNT(p->fontdata)++; - charcnt = FNTCHARCNT(p->fontdata); - } - con_copy_unimap(vc->vc_num, i); + /* If we are not the first console on this + fb, copy the font from that console */ + t = &fb_display[display_fg]; + vc->vc_font.width = (*default_mode)->vc_font.width; + vc->vc_font.height = (*default_mode)->vc_font.height; + vc->vc_font.data = p->fontdata = t->fontdata; + p->userfont = t->userfont; + if (p->userfont) { + REFCOUNT(p->fontdata)++; + charcnt = FNTCHARCNT(p->fontdata); } + con_copy_unimap(vc->vc_num, display_fg); - if (!p->fontdata) { - if (!p->fontname[0] || !(font = find_font(p->fontname))) - font = get_default_font(info->var.xres, - info->var.yres); - vc->vc_font.width = font->width; - vc->vc_font.height = font->height; - vc->vc_font.data = p->fontdata = font->data; + vc->vc_can_do_color = info->var.bits_per_pixel != 1; + vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; + if (charcnt == 256) { + vc->vc_hi_font_mask = 0; + } else { + vc->vc_hi_font_mask = 0x100; + if (vc->vc_can_do_color) + vc->vc_complement_mask <<= 1; } - updatescrollmode(p, vc); + cols = info->var.xres / vc->vc_font.width; + rows = info->var.yres / vc->vc_font.height; + vc_resize(vc->vc_num, cols, rows); - old_cols = vc->vc_cols; - old_rows = vc->vc_rows; + if (info->var.accel_flags) + p->scrollmode = SCROLL_YNOMOVE; + else + p->scrollmode = SCROLL_YREDRAW; - nr_cols = info->var.xres / vc->vc_font.width; - nr_rows = info->var.yres / vc->vc_font.height; + /* + * ++guenther: console.c:vc_allocate() relies on initializing + * vc_{cols,rows}, but we must not set those if we are only + * resizing the console. + */ + if (init) { + vc->vc_cols = cols; + vc->vc_rows = rows; + } if (logo) { /* Need to make room for the logo */ @@ -701,34 +659,28 @@ static void fbcon_set_display(struct vc_ logo_lines = (logo_height + vc->vc_font.height - 1) / vc->vc_font.height; q = (unsigned short *) (vc->vc_origin + - vc->vc_size_row * old_rows); - step = logo_lines * old_cols; - for (r = q - logo_lines * old_cols; r < q; r++) + vc->vc_size_row * rows); + step = logo_lines * cols; + for (r = q - logo_lines * cols; r < q; r++) if (scr_readw(r) != vc->vc_video_erase_char) break; - if (r != q && nr_rows >= old_rows + logo_lines) { - save = - kmalloc(logo_lines * nr_cols * 2, GFP_KERNEL); + if (r != q && rows >= rows + logo_lines) { + save = kmalloc(logo_lines * cols * 2, GFP_KERNEL); if (save) { - int i = - old_cols < - nr_cols ? old_cols : nr_cols; scr_memsetw(save, vc->vc_video_erase_char, - logo_lines * nr_cols * 2); + logo_lines * cols * 2); r = q - step; - for (cnt = 0; cnt < logo_lines; - cnt++, r += i) - scr_memcpyw(save + cnt * nr_cols, - r, 2 * i); + for (cnt = 0; cnt < logo_lines; cnt++, r += cols) + scr_memcpyw(save + cnt * cols, r, 2 * cols); r = q; } } if (r == q) { /* We can scroll screen down */ - r = q - step - old_cols; - for (cnt = old_rows - logo_lines; cnt > 0; cnt--) { + r = q - step - cols; + for (cnt = rows - logo_lines; cnt > 0; cnt--) { scr_memcpyw(r + step, r, vc->vc_size_row); - r -= old_cols; + r -= cols; } if (!save) { vc->vc_y += logo_lines; @@ -738,40 +690,16 @@ static void fbcon_set_display(struct vc_ scr_memsetw((unsigned short *) vc->vc_origin, vc->vc_video_erase_char, vc->vc_size_row * logo_lines); - } - /* - * ++guenther: console.c:vc_allocate() relies on initializing - * vc_{cols,rows}, but we must not set those if we are only - * resizing the console. - */ - if (init) { - vc->vc_cols = nr_cols; - vc->vc_rows = nr_rows; - } - vc->vc_can_do_color = info->var.bits_per_pixel != 1; - vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; - if (charcnt == 256) { - vc->vc_hi_font_mask = 0; - } else { - vc->vc_hi_font_mask = 0x100; - if (vc->vc_can_do_color) - vc->vc_complement_mask <<= 1; - } - - if (logo) { - if (vc->vc_cols != nr_cols || vc->vc_rows != nr_rows) - vc_resize(vc->vc_num, nr_cols, nr_rows); - else if (CON_IS_VISIBLE(vc) && - vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { + if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { accel_clear_margins(vc, info, 0); update_screen(vc->vc_num); } if (save) { q = (unsigned short *) (vc->vc_origin + vc->vc_size_row * - old_rows); - scr_memcpyw(q, save, logo_lines * nr_cols * 2); + rows); + scr_memcpyw(q, save, logo_lines * cols * 2); vc->vc_y += logo_lines; vc->vc_pos += logo_lines * vc->vc_size_row; kfree(save); @@ -786,7 +714,7 @@ static void fbcon_set_display(struct vc_ } } - if (vc->vc_num == fg_console && softback_buf) { + if (vc->vc_num == display_fg && softback_buf) { int l = fbcon_softback_size / vc->vc_size_row; if (l > 5) softback_end = softback_buf + l * vc->vc_size_row; @@ -798,6 +726,12 @@ static void fbcon_set_display(struct vc_ } } +static void fbcon_deinit(struct vc_data *vc) +{ + struct display *p = &fb_display[vc->vc_num]; + + fbcon_free_font(p); +} /* ====================================================================== */ @@ -1531,6 +1465,25 @@ static void fbcon_bmove_rec(struct vc_da height, width); } +static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, struct vc_data *vc) +{ + int m; + + if (p->scrollmode & __SCROLL_YFIXED) + return; + if (divides(info->fix.ywrapstep, vc->vc_font.height) && + divides(vc->vc_font.height, info->var.yres_virtual)) + m = __SCROLL_YWRAP; + else if (divides(info->fix.ypanstep, vc->vc_font.height) && + info->var.yres_virtual >= info->var.yres + vc->vc_font.height) + m = __SCROLL_YPAN; + else if (p->scrollmode & __SCROLL_YNOMOVE) + m = __SCROLL_YREDRAW; + else + m = __SCROLL_YMOVE; + p->scrollmode = (p->scrollmode & ~__SCROLL_YMASK) | m; +} + static int fbcon_resize(struct vc_data *vc, unsigned int width, unsigned int height) { @@ -1545,22 +1498,30 @@ static int fbcon_resize(struct vc_data * var.yres = height * fh; x_diff = info->var.xres - var.xres; y_diff = info->var.yres - var.yres; - if (x_diff < 0 || x_diff > fw || - (y_diff < 0 || y_diff > fh)) { - var.activate = FB_ACTIVATE_TEST; - err = fb_set_var(info, &var); - if (err || width > var.xres/fw || - height > var.yres/fh) + if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) { + char mode[40]; + + DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); + if (!info->fbops->fb_set_par) + return -EINVAL; + + sprintf(mode, "%dx%d", var.xres, var.yres); + err = fb_find_mode(&var, info, mode, NULL, 0, NULL, + info->var.bits_per_pixel); + if (!err || width > var.xres/fw || height > var.yres/fh) return -EINVAL; DPRINTK("resize now %ix%i\n", var.xres, var.yres); - var.activate = FB_ACTIVATE_NOW; - fb_set_var(info, &var); + if (CON_IS_VISIBLE(vc)) { + var.activate = FB_ACTIVATE_NOW; + fb_set_var(info, &var); + } } p->vrows = var.yres_virtual/fh; if (var.yres > (fh * (height + 1))) p->vrows -= (var.yres - (fh * height)) / fh; if ((var.yres % fh) && (var.yres_virtual % fh < var.yres % fh)) p->vrows--; + updatescrollmode(p, info, vc); return 0; } @@ -1830,7 +1791,6 @@ static int fbcon_do_set_font(struct vc_d if (resize) { /* reset wrap/pan */ info->var.xoffset = info->var.yoffset = p->yscroll = 0; - updatescrollmode(p, vc); vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h); if (CON_IS_VISIBLE(vc) && softback_buf) { int l = fbcon_softback_size / vc->vc_size_row; diff -puN drivers/video/console/fbcon.h~fbdev-mode-switching-fix drivers/video/console/fbcon.h --- 25/drivers/video/console/fbcon.h~fbdev-mode-switching-fix Wed May 19 15:02:09 2004 +++ 25-akpm/drivers/video/console/fbcon.h Wed May 19 15:02:09 2004 @@ -27,7 +27,6 @@ struct display { /* Filled in by the frame buffer device */ u_short inverse; /* != 0 text black on white as default */ /* Filled in by the low-level console driver */ - char fontname[40]; /* Font associated to this display */ u_char *fontdata; int userfont; /* != 0 if fontdata kmalloc()ed */ u_short scrollmode; /* Scroll Method */ diff -puN drivers/video/modedb.c~fbdev-mode-switching-fix drivers/video/modedb.c --- 25/drivers/video/modedb.c~fbdev-mode-switching-fix Wed May 19 15:02:09 2004 +++ 25-akpm/drivers/video/modedb.c Wed May 19 15:02:09 2004 @@ -391,7 +391,7 @@ static int my_atoi(const char *name) } /** - * __fb_try_mode - test a video mode + * fb_try_mode - test a video mode * @var: frame buffer user defined part of display * @info: frame buffer info structure * @mode: frame buffer video mode structure @@ -403,10 +403,10 @@ static int my_atoi(const char *name) * */ -int __fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info, +int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info, const struct fb_videomode *mode, unsigned int bpp) { - int err = 1; + int err = 0; DPRINTK("Trying mode %s %dx%d-%d@%d\n", mode->name ? mode->name : "noname", mode->xres, mode->yres, bpp, mode->refresh); @@ -430,10 +430,9 @@ int __fb_try_mode(struct fb_var_screenin if (info->fbops->fb_check_var) err = info->fbops->fb_check_var(var, info); var->activate &= ~FB_ACTIVATE_TEST; - return !err; + return err; } - /** * fb_find_mode - finds a valid video mode * @var: frame buffer user defined part of display @@ -536,18 +535,18 @@ done: if ((name_matches(db[j], name, namelen) || (res_specified && res_matches(db[j], xres, yres))) && (!i || db[j].refresh == refresh) && - __fb_try_mode(var, info, &db[j], bpp)) + !fb_try_mode(var, info, &db[j], bpp)) return 2-i; } } DPRINTK("Trying default video mode\n"); - if (__fb_try_mode(var, info, default_mode, default_bpp)) + if (!fb_try_mode(var, info, default_mode, default_bpp)) return 3; DPRINTK("Trying all modes\n"); for (i = 0; i < dbsize; i++) - if (__fb_try_mode(var, info, &db[i], default_bpp)) + if (!fb_try_mode(var, info, &db[i], default_bpp)) return 4; DPRINTK("No valid mode found\n"); diff -puN drivers/video/radeonfb.c~fbdev-mode-switching-fix drivers/video/radeonfb.c --- 25/drivers/video/radeonfb.c~fbdev-mode-switching-fix Wed May 19 15:02:09 2004 +++ 25-akpm/drivers/video/radeonfb.c Wed May 19 15:02:09 2004 @@ -2252,7 +2252,6 @@ static int __devinit radeon_set_fbinfo ( info->pseudo_palette = rinfo->pseudo_palette; info->flags = FBINFO_FLAG_DEFAULT; info->fbops = &radeonfb_ops; - info->display_fg = NULL; info->screen_base = (char *)rinfo->fb_base; /* Fill fix common fields */ diff -puN include/linux/fb.h~fbdev-mode-switching-fix include/linux/fb.h --- 25/include/linux/fb.h~fbdev-mode-switching-fix Wed May 19 15:02:09 2004 +++ 25-akpm/include/linux/fb.h Wed May 19 15:02:09 2004 @@ -516,7 +516,6 @@ struct fb_info { struct fb_cmap cmap; /* Current cmap */ struct fb_ops *fbops; char *screen_base; /* Virtual address */ - struct vc_data *display_fg; /* Console visible on this display */ int currcon; /* Current VC. */ void *pseudo_palette; /* Fake palette of 16 colors */ #define FBINFO_STATE_RUNNING 0 _