18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * linux/drivers/video/vgastate.c -- VGA state save/restore
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2002 James Simmons
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright history from vga16fb.c:
78c2ecf20Sopenharmony_ci *	Copyright 1999 Ben Pfaff and Petr Vandrovec
88c2ecf20Sopenharmony_ci *	Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
98c2ecf20Sopenharmony_ci *	Based on VESA framebuffer (c) 1998 Gerd Knorr
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
128c2ecf20Sopenharmony_ci * Public License.  See the file COPYING in the main directory of this
138c2ecf20Sopenharmony_ci * archive for more details.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/fb.h>
198c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
208c2ecf20Sopenharmony_ci#include <video/vga.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct regstate {
238c2ecf20Sopenharmony_ci	__u8 *vga_font0;
248c2ecf20Sopenharmony_ci	__u8 *vga_font1;
258c2ecf20Sopenharmony_ci	__u8 *vga_text;
268c2ecf20Sopenharmony_ci	__u8 *vga_cmap;
278c2ecf20Sopenharmony_ci	__u8 *attr;
288c2ecf20Sopenharmony_ci	__u8 *crtc;
298c2ecf20Sopenharmony_ci	__u8 *gfx;
308c2ecf20Sopenharmony_ci	__u8 *seq;
318c2ecf20Sopenharmony_ci	__u8 misc;
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic inline unsigned char vga_rcrtcs(void __iomem *regbase, unsigned short iobase,
358c2ecf20Sopenharmony_ci				       unsigned char reg)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	vga_w(regbase, iobase + 0x4, reg);
388c2ecf20Sopenharmony_ci	return vga_r(regbase, iobase + 0x5);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline void vga_wcrtcs(void __iomem *regbase, unsigned short iobase,
428c2ecf20Sopenharmony_ci			      unsigned char reg, unsigned char val)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	vga_w(regbase, iobase + 0x4, reg);
458c2ecf20Sopenharmony_ci	vga_w(regbase, iobase + 0x5, val);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void save_vga_text(struct vgastate *state, void __iomem *fbbase)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
518c2ecf20Sopenharmony_ci	int i;
528c2ecf20Sopenharmony_ci	u8 misc, attr10, gr4, gr5, gr6, seq1, seq2, seq4;
538c2ecf20Sopenharmony_ci	unsigned short iobase;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* if in graphics mode, no need to save */
568c2ecf20Sopenharmony_ci	misc = vga_r(state->vgabase, VGA_MIS_R);
578c2ecf20Sopenharmony_ci	iobase = (misc & 1) ? 0x3d0 : 0x3b0;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
608c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x00);
618c2ecf20Sopenharmony_ci	attr10 = vga_rattr(state->vgabase, 0x10);
628c2ecf20Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
638c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x20);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (attr10 & 1)
668c2ecf20Sopenharmony_ci		return;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* save regs */
698c2ecf20Sopenharmony_ci	gr4 = vga_rgfx(state->vgabase, VGA_GFX_PLANE_READ);
708c2ecf20Sopenharmony_ci	gr5 = vga_rgfx(state->vgabase, VGA_GFX_MODE);
718c2ecf20Sopenharmony_ci	gr6 = vga_rgfx(state->vgabase, VGA_GFX_MISC);
728c2ecf20Sopenharmony_ci	seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE);
738c2ecf20Sopenharmony_ci	seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* blank screen */
768c2ecf20Sopenharmony_ci	seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
778c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
788c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 | 1 << 5);
798c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* save font at plane 2 */
828c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT0) {
838c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x4);
848c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
858c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2);
868c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
878c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
888c2ecf20Sopenharmony_ci		for (i = 0; i < 4 * 8192; i++)
898c2ecf20Sopenharmony_ci			saved->vga_font0[i] = vga_r(fbbase, i);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* save font at plane 3 */
938c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT1) {
948c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x8);
958c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
968c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3);
978c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
988c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
998c2ecf20Sopenharmony_ci		for (i = 0; i < state->memsize; i++)
1008c2ecf20Sopenharmony_ci			saved->vga_font1[i] = vga_r(fbbase, i);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* save font at plane 0/1 */
1048c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_TEXT) {
1058c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1);
1068c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
1078c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0);
1088c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
1098c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
1108c2ecf20Sopenharmony_ci		for (i = 0; i < 8192; i++)
1118c2ecf20Sopenharmony_ci			saved->vga_text[i] = vga_r(fbbase, i);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2);
1148c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
1158c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1);
1168c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
1178c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
1188c2ecf20Sopenharmony_ci		for (i = 0; i < 8192; i++)
1198c2ecf20Sopenharmony_ci			saved->vga_text[8192+i] = vga_r(fbbase + 2 * 8192, i);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* restore regs */
1238c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, seq2);
1248c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, seq4);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, gr4);
1278c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MODE, gr5);
1288c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MISC, gr6);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* unblank screen */
1318c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
1328c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 & ~(1 << 5));
1338c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void restore_vga_text(struct vgastate *state, void __iomem *fbbase)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
1418c2ecf20Sopenharmony_ci	int i;
1428c2ecf20Sopenharmony_ci	u8 gr1, gr3, gr4, gr5, gr6, gr8;
1438c2ecf20Sopenharmony_ci	u8 seq1, seq2, seq4;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* save regs */
1468c2ecf20Sopenharmony_ci	gr1 = vga_rgfx(state->vgabase, VGA_GFX_SR_ENABLE);
1478c2ecf20Sopenharmony_ci	gr3 = vga_rgfx(state->vgabase, VGA_GFX_DATA_ROTATE);
1488c2ecf20Sopenharmony_ci	gr4 = vga_rgfx(state->vgabase, VGA_GFX_PLANE_READ);
1498c2ecf20Sopenharmony_ci	gr5 = vga_rgfx(state->vgabase, VGA_GFX_MODE);
1508c2ecf20Sopenharmony_ci	gr6 = vga_rgfx(state->vgabase, VGA_GFX_MISC);
1518c2ecf20Sopenharmony_ci	gr8 = vga_rgfx(state->vgabase, VGA_GFX_BIT_MASK);
1528c2ecf20Sopenharmony_ci	seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE);
1538c2ecf20Sopenharmony_ci	seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* blank screen */
1568c2ecf20Sopenharmony_ci	seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
1578c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
1588c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 | 1 << 5);
1598c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (state->depth == 4) {
1628c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_DATA_ROTATE, 0x0);
1638c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_BIT_MASK, 0xff);
1648c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_SR_ENABLE, 0x00);
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* restore font at plane 2 */
1688c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT0) {
1698c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x4);
1708c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
1718c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2);
1728c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
1738c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
1748c2ecf20Sopenharmony_ci		for (i = 0; i < 4 * 8192; i++)
1758c2ecf20Sopenharmony_ci			vga_w(fbbase, i, saved->vga_font0[i]);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* restore font at plane 3 */
1798c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_FONT1) {
1808c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x8);
1818c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
1828c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3);
1838c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
1848c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
1858c2ecf20Sopenharmony_ci		for (i = 0; i < state->memsize; i++)
1868c2ecf20Sopenharmony_ci			vga_w(fbbase, i, saved->vga_font1[i]);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* restore font at plane 0/1 */
1908c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_TEXT) {
1918c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1);
1928c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
1938c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0);
1948c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
1958c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
1968c2ecf20Sopenharmony_ci		for (i = 0; i < 8192; i++)
1978c2ecf20Sopenharmony_ci			vga_w(fbbase, i, saved->vga_text[i]);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2);
2008c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6);
2018c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1);
2028c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0);
2038c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5);
2048c2ecf20Sopenharmony_ci		for (i = 0; i < 8192; i++)
2058c2ecf20Sopenharmony_ci			vga_w(fbbase, i, saved->vga_text[8192+i]);
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* unblank screen */
2098c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
2108c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1 & ~(1 << 5));
2118c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x3);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* restore regs */
2148c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_SR_ENABLE, gr1);
2158c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_DATA_ROTATE, gr3);
2168c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, gr4);
2178c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MODE, gr5);
2188c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_MISC, gr6);
2198c2ecf20Sopenharmony_ci	vga_wgfx(state->vgabase, VGA_GFX_BIT_MASK, gr8);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, seq1);
2228c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, seq2);
2238c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, seq4);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic void save_vga_mode(struct vgastate *state)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
2298c2ecf20Sopenharmony_ci	unsigned short iobase;
2308c2ecf20Sopenharmony_ci	int i;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	saved->misc = vga_r(state->vgabase, VGA_MIS_R);
2338c2ecf20Sopenharmony_ci	if (saved->misc & 1)
2348c2ecf20Sopenharmony_ci		iobase = 0x3d0;
2358c2ecf20Sopenharmony_ci	else
2368c2ecf20Sopenharmony_ci		iobase = 0x3b0;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	for (i = 0; i < state->num_crtc; i++)
2398c2ecf20Sopenharmony_ci		saved->crtc[i] = vga_rcrtcs(state->vgabase, iobase, i);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
2428c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x00);
2438c2ecf20Sopenharmony_ci	for (i = 0; i < state->num_attr; i++) {
2448c2ecf20Sopenharmony_ci		vga_r(state->vgabase, iobase + 0xa);
2458c2ecf20Sopenharmony_ci		saved->attr[i] = vga_rattr(state->vgabase, i);
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
2488c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x20);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	for (i = 0; i < state->num_gfx; i++)
2518c2ecf20Sopenharmony_ci		saved->gfx[i] = vga_rgfx(state->vgabase, i);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for (i = 0; i < state->num_seq; i++)
2548c2ecf20Sopenharmony_ci		saved->seq[i] = vga_rseq(state->vgabase, i);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void restore_vga_mode(struct vgastate *state)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
2608c2ecf20Sopenharmony_ci	unsigned short iobase;
2618c2ecf20Sopenharmony_ci	int i;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_MIS_W, saved->misc);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (saved->misc & 1)
2668c2ecf20Sopenharmony_ci		iobase = 0x3d0;
2678c2ecf20Sopenharmony_ci	else
2688c2ecf20Sopenharmony_ci		iobase = 0x3b0;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* turn off display */
2718c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE,
2728c2ecf20Sopenharmony_ci		 saved->seq[VGA_SEQ_CLOCK_MODE] | 0x20);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* disable sequencer */
2758c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* enable palette addressing */
2788c2ecf20Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
2798c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x00);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	for (i = 2; i < state->num_seq; i++)
2828c2ecf20Sopenharmony_ci		vga_wseq(state->vgabase, i, saved->seq[i]);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* unprotect vga regs */
2868c2ecf20Sopenharmony_ci	vga_wcrtcs(state->vgabase, iobase, 17, saved->crtc[17] & ~0x80);
2878c2ecf20Sopenharmony_ci	for (i = 0; i < state->num_crtc; i++)
2888c2ecf20Sopenharmony_ci		vga_wcrtcs(state->vgabase, iobase, i, saved->crtc[i]);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	for (i = 0; i < state->num_gfx; i++)
2918c2ecf20Sopenharmony_ci		vga_wgfx(state->vgabase, i, saved->gfx[i]);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	for (i = 0; i < state->num_attr; i++) {
2948c2ecf20Sopenharmony_ci		vga_r(state->vgabase, iobase + 0xa);
2958c2ecf20Sopenharmony_ci		vga_wattr(state->vgabase, i, saved->attr[i]);
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* reenable sequencer */
2998c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
3008c2ecf20Sopenharmony_ci	/* turn display on */
3018c2ecf20Sopenharmony_ci	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE,
3028c2ecf20Sopenharmony_ci		 saved->seq[VGA_SEQ_CLOCK_MODE] & ~(1 << 5));
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* disable video/palette source */
3058c2ecf20Sopenharmony_ci	vga_r(state->vgabase, iobase + 0xa);
3068c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_ATT_W, 0x20);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic void save_vga_cmap(struct vgastate *state)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
3128c2ecf20Sopenharmony_ci	int i;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* assumes DAC is readable and writable */
3178c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_IR, 0x00);
3188c2ecf20Sopenharmony_ci	for (i = 0; i < 768; i++)
3198c2ecf20Sopenharmony_ci		saved->vga_cmap[i] = vga_r(state->vgabase, VGA_PEL_D);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void restore_vga_cmap(struct vgastate *state)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct regstate *saved = (struct regstate *) state->vidstate;
3258c2ecf20Sopenharmony_ci	int i;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* assumes DAC is readable and writable */
3308c2ecf20Sopenharmony_ci	vga_w(state->vgabase, VGA_PEL_IW, 0x00);
3318c2ecf20Sopenharmony_ci	for (i = 0; i < 768; i++)
3328c2ecf20Sopenharmony_ci		vga_w(state->vgabase, VGA_PEL_D, saved->vga_cmap[i]);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic void vga_cleanup(struct vgastate *state)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	if (state->vidstate != NULL) {
3388c2ecf20Sopenharmony_ci		struct regstate *saved = (struct regstate *) state->vidstate;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		vfree(saved->vga_font0);
3418c2ecf20Sopenharmony_ci		vfree(saved->vga_font1);
3428c2ecf20Sopenharmony_ci		vfree(saved->vga_text);
3438c2ecf20Sopenharmony_ci		vfree(saved->vga_cmap);
3448c2ecf20Sopenharmony_ci		vfree(saved->attr);
3458c2ecf20Sopenharmony_ci		kfree(saved);
3468c2ecf20Sopenharmony_ci		state->vidstate = NULL;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ciint save_vga(struct vgastate *state)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct regstate *saved;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	saved = kzalloc(sizeof(struct regstate), GFP_KERNEL);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (saved == NULL)
3578c2ecf20Sopenharmony_ci		return 1;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	state->vidstate = (void *)saved;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_CMAP) {
3628c2ecf20Sopenharmony_ci		saved->vga_cmap = vmalloc(768);
3638c2ecf20Sopenharmony_ci		if (!saved->vga_cmap) {
3648c2ecf20Sopenharmony_ci			vga_cleanup(state);
3658c2ecf20Sopenharmony_ci			return 1;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci		save_vga_cmap(state);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_MODE) {
3718c2ecf20Sopenharmony_ci		int total;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		if (state->num_attr < 21)
3748c2ecf20Sopenharmony_ci			state->num_attr = 21;
3758c2ecf20Sopenharmony_ci		if (state->num_crtc < 25)
3768c2ecf20Sopenharmony_ci			state->num_crtc = 25;
3778c2ecf20Sopenharmony_ci		if (state->num_gfx < 9)
3788c2ecf20Sopenharmony_ci			state->num_gfx = 9;
3798c2ecf20Sopenharmony_ci		if (state->num_seq < 5)
3808c2ecf20Sopenharmony_ci			state->num_seq = 5;
3818c2ecf20Sopenharmony_ci		total = state->num_attr + state->num_crtc +
3828c2ecf20Sopenharmony_ci			state->num_gfx + state->num_seq;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		saved->attr = vmalloc(total);
3858c2ecf20Sopenharmony_ci		if (!saved->attr) {
3868c2ecf20Sopenharmony_ci			vga_cleanup(state);
3878c2ecf20Sopenharmony_ci			return 1;
3888c2ecf20Sopenharmony_ci		}
3898c2ecf20Sopenharmony_ci		saved->crtc = saved->attr + state->num_attr;
3908c2ecf20Sopenharmony_ci		saved->gfx = saved->crtc + state->num_crtc;
3918c2ecf20Sopenharmony_ci		saved->seq = saved->gfx + state->num_gfx;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		save_vga_mode(state);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_FONTS) {
3978c2ecf20Sopenharmony_ci		void __iomem *fbbase;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		/* exit if window is less than 32K */
4008c2ecf20Sopenharmony_ci		if (state->memsize && state->memsize < 4 * 8192) {
4018c2ecf20Sopenharmony_ci			vga_cleanup(state);
4028c2ecf20Sopenharmony_ci			return 1;
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci		if (!state->memsize)
4058c2ecf20Sopenharmony_ci			state->memsize = 8 * 8192;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		if (!state->membase)
4088c2ecf20Sopenharmony_ci			state->membase = 0xA0000;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		fbbase = ioremap(state->membase, state->memsize);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		if (!fbbase) {
4138c2ecf20Sopenharmony_ci			vga_cleanup(state);
4148c2ecf20Sopenharmony_ci			return 1;
4158c2ecf20Sopenharmony_ci		}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci		/*
4188c2ecf20Sopenharmony_ci		 * save only first 32K used by vgacon
4198c2ecf20Sopenharmony_ci		 */
4208c2ecf20Sopenharmony_ci		if (state->flags & VGA_SAVE_FONT0) {
4218c2ecf20Sopenharmony_ci			saved->vga_font0 = vmalloc(4 * 8192);
4228c2ecf20Sopenharmony_ci			if (!saved->vga_font0) {
4238c2ecf20Sopenharmony_ci				iounmap(fbbase);
4248c2ecf20Sopenharmony_ci				vga_cleanup(state);
4258c2ecf20Sopenharmony_ci				return 1;
4268c2ecf20Sopenharmony_ci			}
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci		/*
4298c2ecf20Sopenharmony_ci		 * largely unused, but if required by the caller
4308c2ecf20Sopenharmony_ci		 * we'll just save everything.
4318c2ecf20Sopenharmony_ci		 */
4328c2ecf20Sopenharmony_ci		if (state->flags & VGA_SAVE_FONT1) {
4338c2ecf20Sopenharmony_ci			saved->vga_font1 = vmalloc(state->memsize);
4348c2ecf20Sopenharmony_ci			if (!saved->vga_font1) {
4358c2ecf20Sopenharmony_ci				iounmap(fbbase);
4368c2ecf20Sopenharmony_ci				vga_cleanup(state);
4378c2ecf20Sopenharmony_ci				return 1;
4388c2ecf20Sopenharmony_ci			}
4398c2ecf20Sopenharmony_ci		}
4408c2ecf20Sopenharmony_ci		/*
4418c2ecf20Sopenharmony_ci		 * Save 8K at plane0[0], and 8K at plane1[16K]
4428c2ecf20Sopenharmony_ci		 */
4438c2ecf20Sopenharmony_ci		if (state->flags & VGA_SAVE_TEXT) {
4448c2ecf20Sopenharmony_ci			saved->vga_text = vmalloc(8192 * 2);
4458c2ecf20Sopenharmony_ci			if (!saved->vga_text) {
4468c2ecf20Sopenharmony_ci				iounmap(fbbase);
4478c2ecf20Sopenharmony_ci				vga_cleanup(state);
4488c2ecf20Sopenharmony_ci				return 1;
4498c2ecf20Sopenharmony_ci			}
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		save_vga_text(state, fbbase);
4538c2ecf20Sopenharmony_ci		iounmap(fbbase);
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci	return 0;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ciint restore_vga(struct vgastate *state)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	if (state->vidstate == NULL)
4618c2ecf20Sopenharmony_ci		return 1;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_MODE)
4648c2ecf20Sopenharmony_ci		restore_vga_mode(state);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_FONTS) {
4678c2ecf20Sopenharmony_ci		void __iomem *fbbase = ioremap(state->membase, state->memsize);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		if (!fbbase) {
4708c2ecf20Sopenharmony_ci			vga_cleanup(state);
4718c2ecf20Sopenharmony_ci			return 1;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci		restore_vga_text(state, fbbase);
4748c2ecf20Sopenharmony_ci		iounmap(fbbase);
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (state->flags & VGA_SAVE_CMAP)
4788c2ecf20Sopenharmony_ci		restore_vga_cmap(state);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	vga_cleanup(state);
4818c2ecf20Sopenharmony_ci	return 0;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(save_vga);
4858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(restore_vga);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ciMODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
4888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VGA State Save/Restore");
4898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4908c2ecf20Sopenharmony_ci
491