162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * linux/drivers/video/vgastate.c -- VGA state save/restore
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 2002 James Simmons
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright history from vga16fb.c:
762306a36Sopenharmony_ci *	Copyright 1999 Ben Pfaff and Petr Vandrovec
862306a36Sopenharmony_ci *	Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
962306a36Sopenharmony_ci *	Based on VESA framebuffer (c) 1998 Gerd Knorr
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
1262306a36Sopenharmony_ci * Public License.  See the file COPYING in the main directory of this
1362306a36Sopenharmony_ci * archive for more details.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/fb.h>
1962306a36Sopenharmony_ci#include <linux/vmalloc.h>
2062306a36Sopenharmony_ci#include <video/vga.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct regstate {
2362306a36Sopenharmony_ci	__u8 *vga_font0;
2462306a36Sopenharmony_ci	__u8 *vga_font1;
2562306a36Sopenharmony_ci	__u8 *vga_text;
2662306a36Sopenharmony_ci	__u8 *vga_cmap;
2762306a36Sopenharmony_ci	__u8 *attr;
2862306a36Sopenharmony_ci	__u8 *crtc;
2962306a36Sopenharmony_ci	__u8 *gfx;
3062306a36Sopenharmony_ci	__u8 *seq;
3162306a36Sopenharmony_ci	__u8 misc;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline unsigned char vga_rcrtcs(void __iomem *regbase, unsigned short iobase,
3562306a36Sopenharmony_ci				       unsigned char reg)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	vga_w(regbase, iobase + 0x4, reg);
3862306a36Sopenharmony_ci	return vga_r(regbase, iobase + 0x5);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic inline void vga_wcrtcs(void __iomem *regbase, unsigned short iobase,
4262306a36Sopenharmony_ci			      unsigned char reg, unsigned char val)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	vga_w(regbase, iobase + 0x4, reg);
4562306a36Sopenharmony_ci	vga_w(regbase, iobase + 0x5, val);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void save_vga_text(struct vgastate *state, void __iomem *fbbase)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
5162306a36Sopenharmony_ci	int i;
5262306a36Sopenharmony_ci	u8 misc, attr10, gr4, gr5, gr6, seq1, seq2, seq4;
5362306a36Sopenharmony_ci	unsigned short iobase;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* if in graphics mode, no need to save */
5662306a36Sopenharmony_ci	misc = vga_r(state->vgabase, VGA_MIS_R);
5762306a36Sopenharmony_ci	iobase = (misc & 1) ? 0x3d0 : 0x3b0;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
6062306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x00);
6162306a36Sopenharmony_ci	attr10 = vga_rattr(state->vgabase, 0x10);
6262306a36Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
6362306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x20);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (attr10 & 1)
6662306a36Sopenharmony_ci		return;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* save regs */
6962306a36Sopenharmony_ci	gr4 = vga_rgfx(state->vgabase, VGA_GFX_PLANE_READ);
7062306a36Sopenharmony_ci	gr5 = vga_rgfx(state->vgabase, VGA_GFX_MODE);
7162306a36Sopenharmony_ci	gr6 = vga_rgfx(state->vgabase, VGA_GFX_MISC);
7262306a36Sopenharmony_ci	seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE);
7362306a36Sopenharmony_ci	seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* blank screen */
7662306a36Sopenharmony_ci	seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
7762306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
7862306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 | 1 << 5);
7962306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* save font at plane 2 */
8262306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT0) {
8362306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x4);
8462306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
8562306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2);
8662306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
8762306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
8862306a36Sopenharmony_ci		for (i = 0; i < 4 * 8192; i++)
8962306a36Sopenharmony_ci			saved->vga_font0[i] = vga_r(fbbase, i);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* save font at plane 3 */
9362306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT1) {
9462306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x8);
9562306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
9662306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3);
9762306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
9862306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
9962306a36Sopenharmony_ci		for (i = 0; i < state->memsize; i++)
10062306a36Sopenharmony_ci			saved->vga_font1[i] = vga_r(fbbase, i);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* save font at plane 0/1 */
10462306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_TEXT) {
10562306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1);
10662306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
10762306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0);
10862306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
10962306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
11062306a36Sopenharmony_ci		for (i = 0; i < 8192; i++)
11162306a36Sopenharmony_ci			saved->vga_text[i] = vga_r(fbbase, i);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2);
11462306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
11562306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1);
11662306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
11762306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
11862306a36Sopenharmony_ci		for (i = 0; i < 8192; i++)
11962306a36Sopenharmony_ci			saved->vga_text[8192+i] = vga_r(fbbase + 2 * 8192, i);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* restore regs */
12362306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, seq2);
12462306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, seq4);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, gr4);
12762306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MODE, gr5);
12862306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MISC, gr6);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* unblank screen */
13162306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
13262306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 & ~(1 << 5));
13362306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void restore_vga_text(struct vgastate *state, void __iomem *fbbase)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
14162306a36Sopenharmony_ci	int i;
14262306a36Sopenharmony_ci	u8 gr1, gr3, gr4, gr5, gr6, gr8;
14362306a36Sopenharmony_ci	u8 seq1, seq2, seq4;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* save regs */
14662306a36Sopenharmony_ci	gr1 = vga_rgfx(state->vgabase, VGA_GFX_SR_ENABLE);
14762306a36Sopenharmony_ci	gr3 = vga_rgfx(state->vgabase, VGA_GFX_DATA_ROTATE);
14862306a36Sopenharmony_ci	gr4 = vga_rgfx(state->vgabase, VGA_GFX_PLANE_READ);
14962306a36Sopenharmony_ci	gr5 = vga_rgfx(state->vgabase, VGA_GFX_MODE);
15062306a36Sopenharmony_ci	gr6 = vga_rgfx(state->vgabase, VGA_GFX_MISC);
15162306a36Sopenharmony_ci	gr8 = vga_rgfx(state->vgabase, VGA_GFX_BIT_MASK);
15262306a36Sopenharmony_ci	seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE);
15362306a36Sopenharmony_ci	seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* blank screen */
15662306a36Sopenharmony_ci	seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
15762306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
15862306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 | 1 << 5);
15962306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (state->depth == 4) {
16262306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_DATA_ROTATE, 0x0);
16362306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_BIT_MASK, 0xff);
16462306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_SR_ENABLE, 0x00);
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* restore font at plane 2 */
16862306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT0) {
16962306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x4);
17062306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
17162306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2);
17262306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
17362306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
17462306a36Sopenharmony_ci		for (i = 0; i < 4 * 8192; i++)
17562306a36Sopenharmony_ci			vga_w(fbbase, i, saved->vga_font0[i]);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* restore font at plane 3 */
17962306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT1) {
18062306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x8);
18162306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
18262306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3);
18362306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
18462306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
18562306a36Sopenharmony_ci		for (i = 0; i < state->memsize; i++)
18662306a36Sopenharmony_ci			vga_w(fbbase, i, saved->vga_font1[i]);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* restore font at plane 0/1 */
19062306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_TEXT) {
19162306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1);
19262306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
19362306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0);
19462306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
19562306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
19662306a36Sopenharmony_ci		for (i = 0; i < 8192; i++)
19762306a36Sopenharmony_ci			vga_w(fbbase, i, saved->vga_text[i]);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2);
20062306a36Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
20162306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1);
20262306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
20362306a36Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
20462306a36Sopenharmony_ci		for (i = 0; i < 8192; i++)
20562306a36Sopenharmony_ci			vga_w(fbbase, i, saved->vga_text[8192+i]);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* unblank screen */
20962306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
21062306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 & ~(1 << 5));
21162306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* restore regs */
21462306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_SR_ENABLE, gr1);
21562306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_DATA_ROTATE, gr3);
21662306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, gr4);
21762306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MODE, gr5);
21862306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MISC, gr6);
21962306a36Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_BIT_MASK, gr8);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1);
22262306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, seq2);
22362306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, seq4);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void save_vga_mode(struct vgastate *state)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
22962306a36Sopenharmony_ci	unsigned short iobase;
23062306a36Sopenharmony_ci	int i;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	saved->misc = vga_r(state->vgabase, VGA_MIS_R);
23362306a36Sopenharmony_ci	if (saved->misc & 1)
23462306a36Sopenharmony_ci		iobase = 0x3d0;
23562306a36Sopenharmony_ci	else
23662306a36Sopenharmony_ci		iobase = 0x3b0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	for (i = 0; i < state->num_crtc; i++)
23962306a36Sopenharmony_ci		saved->crtc[i] = vga_rcrtcs(state->vgabase, iobase, i);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
24262306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x00);
24362306a36Sopenharmony_ci	for (i = 0; i < state->num_attr; i++) {
24462306a36Sopenharmony_ci		vga_r(state->vgabase, iobase + 0xa);
24562306a36Sopenharmony_ci		saved->attr[i] = vga_rattr(state->vgabase, i);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
24862306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x20);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	for (i = 0; i < state->num_gfx; i++)
25162306a36Sopenharmony_ci		saved->gfx[i] = vga_rgfx(state->vgabase, i);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	for (i = 0; i < state->num_seq; i++)
25462306a36Sopenharmony_ci		saved->seq[i] = vga_rseq(state->vgabase, i);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void restore_vga_mode(struct vgastate *state)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
26062306a36Sopenharmony_ci	unsigned short iobase;
26162306a36Sopenharmony_ci	int i;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_MIS_W, saved->misc);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (saved->misc & 1)
26662306a36Sopenharmony_ci		iobase = 0x3d0;
26762306a36Sopenharmony_ci	else
26862306a36Sopenharmony_ci		iobase = 0x3b0;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* turn off display */
27162306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE,
27262306a36Sopenharmony_ci		 saved->seq[VGA_SEQ_CLOCK_MODE] | 0x20);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* disable sequencer */
27562306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* enable palette addressing */
27862306a36Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
27962306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x00);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	for (i = 2; i < state->num_seq; i++)
28262306a36Sopenharmony_ci		vga_wseq(state->vgabase, i, saved->seq[i]);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* unprotect vga regs */
28662306a36Sopenharmony_ci	vga_wcrtcs(state->vgabase, iobase, 17, saved->crtc[17] & ~0x80);
28762306a36Sopenharmony_ci	for (i = 0; i < state->num_crtc; i++)
28862306a36Sopenharmony_ci		vga_wcrtcs(state->vgabase, iobase, i, saved->crtc[i]);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	for (i = 0; i < state->num_gfx; i++)
29162306a36Sopenharmony_ci		vga_wgfx(state->vgabase, i, saved->gfx[i]);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	for (i = 0; i < state->num_attr; i++) {
29462306a36Sopenharmony_ci		vga_r(state->vgabase, iobase + 0xa);
29562306a36Sopenharmony_ci		vga_wattr(state->vgabase, i, saved->attr[i]);
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* reenable sequencer */
29962306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
30062306a36Sopenharmony_ci	/* turn display on */
30162306a36Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE,
30262306a36Sopenharmony_ci		 saved->seq[VGA_SEQ_CLOCK_MODE] & ~(1 << 5));
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* disable video/palette source */
30562306a36Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
30662306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x20);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic void save_vga_cmap(struct vgastate *state)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
31262306a36Sopenharmony_ci	int i;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* assumes DAC is readable and writable */
31762306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_IR, 0x00);
31862306a36Sopenharmony_ci	for (i = 0; i < 768; i++)
31962306a36Sopenharmony_ci		saved->vga_cmap[i] = vga_r(state->vgabase, VGA_PEL_D);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic void restore_vga_cmap(struct vgastate *state)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
32562306a36Sopenharmony_ci	int i;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* assumes DAC is readable and writable */
33062306a36Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_IW, 0x00);
33162306a36Sopenharmony_ci	for (i = 0; i < 768; i++)
33262306a36Sopenharmony_ci		vga_w(state->vgabase, VGA_PEL_D, saved->vga_cmap[i]);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic void vga_cleanup(struct vgastate *state)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	if (state->vidstate != NULL) {
33862306a36Sopenharmony_ci		struct regstate *saved = (struct regstate *) state->vidstate;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		vfree(saved->vga_font0);
34162306a36Sopenharmony_ci		vfree(saved->vga_font1);
34262306a36Sopenharmony_ci		vfree(saved->vga_text);
34362306a36Sopenharmony_ci		vfree(saved->vga_cmap);
34462306a36Sopenharmony_ci		vfree(saved->attr);
34562306a36Sopenharmony_ci		kfree(saved);
34662306a36Sopenharmony_ci		state->vidstate = NULL;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ciint save_vga(struct vgastate *state)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct regstate *saved;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	saved = kzalloc(sizeof(struct regstate), GFP_KERNEL);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (saved == NULL)
35762306a36Sopenharmony_ci		return 1;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	state->vidstate = (void *)saved;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_CMAP) {
36262306a36Sopenharmony_ci		saved->vga_cmap = vmalloc(768);
36362306a36Sopenharmony_ci		if (!saved->vga_cmap) {
36462306a36Sopenharmony_ci			vga_cleanup(state);
36562306a36Sopenharmony_ci			return 1;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci		save_vga_cmap(state);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_MODE) {
37162306a36Sopenharmony_ci		int total;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		if (state->num_attr < 21)
37462306a36Sopenharmony_ci			state->num_attr = 21;
37562306a36Sopenharmony_ci		if (state->num_crtc < 25)
37662306a36Sopenharmony_ci			state->num_crtc = 25;
37762306a36Sopenharmony_ci		if (state->num_gfx < 9)
37862306a36Sopenharmony_ci			state->num_gfx = 9;
37962306a36Sopenharmony_ci		if (state->num_seq < 5)
38062306a36Sopenharmony_ci			state->num_seq = 5;
38162306a36Sopenharmony_ci		total = state->num_attr + state->num_crtc +
38262306a36Sopenharmony_ci			state->num_gfx + state->num_seq;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		saved->attr = vmalloc(total);
38562306a36Sopenharmony_ci		if (!saved->attr) {
38662306a36Sopenharmony_ci			vga_cleanup(state);
38762306a36Sopenharmony_ci			return 1;
38862306a36Sopenharmony_ci		}
38962306a36Sopenharmony_ci		saved->crtc = saved->attr + state->num_attr;
39062306a36Sopenharmony_ci		saved->gfx = saved->crtc + state->num_crtc;
39162306a36Sopenharmony_ci		saved->seq = saved->gfx + state->num_gfx;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		save_vga_mode(state);
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_FONTS) {
39762306a36Sopenharmony_ci		void __iomem *fbbase;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		/* exit if window is less than 32K */
40062306a36Sopenharmony_ci		if (state->memsize && state->memsize < 4 * 8192) {
40162306a36Sopenharmony_ci			vga_cleanup(state);
40262306a36Sopenharmony_ci			return 1;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci		if (!state->memsize)
40562306a36Sopenharmony_ci			state->memsize = 8 * 8192;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		if (!state->membase)
40862306a36Sopenharmony_ci			state->membase = 0xA0000;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		fbbase = ioremap(state->membase, state->memsize);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		if (!fbbase) {
41362306a36Sopenharmony_ci			vga_cleanup(state);
41462306a36Sopenharmony_ci			return 1;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		/*
41862306a36Sopenharmony_ci		 * save only first 32K used by vgacon
41962306a36Sopenharmony_ci		 */
42062306a36Sopenharmony_ci		if (state->flags & VGA_SAVE_FONT0) {
42162306a36Sopenharmony_ci			saved->vga_font0 = vmalloc(4 * 8192);
42262306a36Sopenharmony_ci			if (!saved->vga_font0) {
42362306a36Sopenharmony_ci				iounmap(fbbase);
42462306a36Sopenharmony_ci				vga_cleanup(state);
42562306a36Sopenharmony_ci				return 1;
42662306a36Sopenharmony_ci			}
42762306a36Sopenharmony_ci		}
42862306a36Sopenharmony_ci		/*
42962306a36Sopenharmony_ci		 * largely unused, but if required by the caller
43062306a36Sopenharmony_ci		 * we'll just save everything.
43162306a36Sopenharmony_ci		 */
43262306a36Sopenharmony_ci		if (state->flags & VGA_SAVE_FONT1) {
43362306a36Sopenharmony_ci			saved->vga_font1 = vmalloc(state->memsize);
43462306a36Sopenharmony_ci			if (!saved->vga_font1) {
43562306a36Sopenharmony_ci				iounmap(fbbase);
43662306a36Sopenharmony_ci				vga_cleanup(state);
43762306a36Sopenharmony_ci				return 1;
43862306a36Sopenharmony_ci			}
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci		/*
44162306a36Sopenharmony_ci		 * Save 8K at plane0[0], and 8K at plane1[16K]
44262306a36Sopenharmony_ci		 */
44362306a36Sopenharmony_ci		if (state->flags & VGA_SAVE_TEXT) {
44462306a36Sopenharmony_ci			saved->vga_text = vmalloc(8192 * 2);
44562306a36Sopenharmony_ci			if (!saved->vga_text) {
44662306a36Sopenharmony_ci				iounmap(fbbase);
44762306a36Sopenharmony_ci				vga_cleanup(state);
44862306a36Sopenharmony_ci				return 1;
44962306a36Sopenharmony_ci			}
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		save_vga_text(state, fbbase);
45362306a36Sopenharmony_ci		iounmap(fbbase);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci	return 0;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ciint restore_vga(struct vgastate *state)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	if (state->vidstate == NULL)
46162306a36Sopenharmony_ci		return 1;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_MODE)
46462306a36Sopenharmony_ci		restore_vga_mode(state);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_FONTS) {
46762306a36Sopenharmony_ci		void __iomem *fbbase = ioremap(state->membase, state->memsize);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		if (!fbbase) {
47062306a36Sopenharmony_ci			vga_cleanup(state);
47162306a36Sopenharmony_ci			return 1;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		restore_vga_text(state, fbbase);
47462306a36Sopenharmony_ci		iounmap(fbbase);
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (state->flags & VGA_SAVE_CMAP)
47862306a36Sopenharmony_ci		restore_vga_cmap(state);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	vga_cleanup(state);
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ciEXPORT_SYMBOL(save_vga);
48562306a36Sopenharmony_ciEXPORT_SYMBOL(restore_vga);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ciMODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
48862306a36Sopenharmony_ciMODULE_DESCRIPTION("VGA State Save/Restore");
48962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
49062306a36Sopenharmony_ci
491