/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nsc/nsc_gx2_vga.c,v 1.2tsi Exp $ */
/*
 * $Workfile: nsc_gx2_vga.c $
 * $Revision$
 * $Author$
 *
 * This file contains routines to set modes using the VGA registers.  
 * Since this file is for the first generation graphics unit, it interfaces
 * to SoftVGA registers.  It works for both VSA1 and VSA2.
 *
 * NSC_LIC_ALTERNATIVE_PREAMBLE
 *
 * Revision 1.0
 *
 * National Semiconductor Alternative GPL-BSD License
 *
 * National Semiconductor Corporation licenses this software 
 * ("Software"):
 *
 *      nsc XFree86
 *
 * under one of the two following licenses, depending on how the 
 * Software is received by the Licensee.
 * 
 * If this Software is received as part of the Linux Framebuffer or
 * other GPL licensed software, then the GPL license designated 
 * NSC_LIC_GPL applies to this Software; in all other circumstances 
 * then the BSD-style license designated NSC_LIC_BSD shall apply.
 *
 * END_NSC_LIC_ALTERNATIVE_PREAMBLE */

/* NSC_LIC_BSD
 *
 * National Semiconductor Corporation Open Source License for Durango
 *
 * (BSD License with Export Notice)
 *
 * Copyright (c) 1999-2001
 * National Semiconductor Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 *
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer. 
 *
 *   * Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the following 
 *     disclaimer in the documentation and/or other materials provided 
 *     with the distribution. 
 *
 *   * Neither the name of the National Semiconductor Corporation nor 
 *     the names of its contributors may be used to endorse or promote 
 *     products derived from this software without specific prior 
 *     written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * NATIONAL SEMICONDUCTOR CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE,
 * INTELLECTUAL PROPERTY INFRINGEMENT, OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF 
 * YOUR JURISDICTION. It is licensee's responsibility to comply with 
 * any export regulations applicable in licensee's jurisdiction. Under 
 * CURRENT (2001) U.S. export regulations this software 
 * is eligible for export from the U.S. and can be downloaded by or 
 * otherwise exported or reexported worldwide EXCEPT to U.S. embargoed 
 * destinations which include Cuba, Iraq, Libya, North Korea, Iran, 
 * Syria, Sudan, Afghanistan and any other country to which the U.S. 
 * has embargoed goods and services. 
 *
 * END_NSC_LIC_BSD */

/* NSC_LIC_GPL
 *
 * National Semiconductor Corporation Gnu General Public License for Durango
 *
 * (GPL License with Export Notice)
 *
 * Copyright (c) 1999-2001
 * National Semiconductor Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted under the terms of the GNU General 
 * Public License as published by the Free Software Foundation; either 
 * version 2 of the License, or (at your option) any later version  
 *
 * In addition to the terms of the GNU General Public License, neither 
 * the name of the National Semiconductor Corporation nor the names of 
 * its contributors may be used to endorse or promote products derived 
 * from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * NATIONAL SEMICONDUCTOR CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE, 
 * INTELLECTUAL PROPERTY INFRINGEMENT, OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE. See the GNU General Public License for more details. 
 *
 * EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF 
 * YOUR JURISDICTION. It is licensee's responsibility to comply with 
 * any export regulations applicable in licensee's jurisdiction. Under 
 * CURRENT (2001) U.S. export regulations this software 
 * is eligible for export from the U.S. and can be downloaded by or 
 * otherwise exported or reexported worldwide EXCEPT to U.S. embargoed 
 * destinations which include Cuba, Iraq, Libya, North Korea, Iran, 
 * Syria, Sudan, Afghanistan and any other country to which the U.S. 
 * has embargoed goods and services. 
 *
 * You should have received a copy of the GNU General Public License 
 * along with this file; if not, write to the Free Software Foundation, 
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 *
 * END_NSC_LIC_GPL */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* VGA STRUCTURE */

#define GU2_STD_CRTC_REGS 25
#define GU2_EXT_CRTC_REGS 15
#define GU2_GDC_REGS 9
#define GU2_SEQ_REGS 5

#define GU2_VGA_FLAG_MISC_OUTPUT	0x1
#define GU2_VGA_FLAG_STD_CRTC		0x2
#define GU2_VGA_FLAG_EXT_CRTC		0x4
#define GU2_VGA_FLAG_GDC		0x10
#define GU2_VGA_FLAG_SEQ		0x20
#define GU2_VGA_FLAG_PALETTE 	0x40
#define GU2_VGA_FLAG_ATTR 	0x80

static unsigned int GDCregs[10];
static unsigned int SEQregs[10];
static unsigned int palette[256];
static unsigned int ATTRregs[32];
static unsigned char *font_data = NULL;

#define VGA_BLOCK 0x40000		/* 256 k */

void gu2_vga_extcrtc(char offset, int reset);
int gu2_get_vga_active(void);
void gu2_vga_font_data(int flag);
void gu2_set_vga(int reset);
int gu2_vga_seq_blanking(void);
int gu2_vga_attr_ctrl(int reset);
void gu2_vga_to_gfx(void);
void gu2_gfx_to_vga(int vga_mode);
int gu2_vga_seq_reset(int reset);
int gu2_vga_save(gfx_vga_struct * vga, int flags);
void gu2_vga_clear_extended(void);
int gu2_vga_restore(gfx_vga_struct * vga, int flags);

int
gu2_get_vga_active(void)
{
   int data = gfx_read_reg32(MDC_GENERAL_CFG);

   if (data & MDC_GCFG_VGAE)
      return 1;
   return 0;
}

void
gu2_vga_font_data(int flag)
{
   if (flag == 0) {
      if (font_data == NULL) {
	 font_data = malloc(VGA_BLOCK);
      }
      DEBUGMSG(1, (0, X_NONE, "Saving VGA Data\n"));
      memcpy(font_data, gfx_virt_fbptr, VGA_BLOCK);
   } else {
      if (font_data) {
	 DEBUGMSG(1, (0, X_NONE, "Restore VGA Data\n"));
	 memcpy(gfx_virt_fbptr, font_data, VGA_BLOCK);
	 free(font_data);
	 font_data = NULL;
      }
   }
}

void
gu2_set_vga(int reset)
{
   int data = gfx_read_reg32(MDC_GENERAL_CFG);

   if (reset)
      data |= MDC_GCFG_VGAE;
   else
      data &= ~MDC_GCFG_VGAE;
   gfx_write_reg32(MDC_GENERAL_CFG, data);
}

int
gu2_vga_seq_blanking(void)
{
   int tmp;

   gfx_outb(0x3C4, 1);
   tmp = gfx_inb(0x3C5);
   tmp |= 0x20;
   tmp |= tmp << 8;
   gfx_outw(0x3C4, tmp);

   gfx_delay_milliseconds(1);
   return (GFX_STATUS_OK);
}

int
gu2_vga_attr_ctrl(int reset)
{
   (void) gfx_inb(0x3DA);
   gfx_outb(0x3C0, (unsigned char)(reset ? 0x00 : 0x20));
   if (reset)
      (void) gfx_inb(0x3DA);
   return (GFX_STATUS_OK);
}

void
gu2_vga_to_gfx(void)
{
   gu2_vga_attr_ctrl(0);

   gu2_vga_seq_blanking();
   gfx_delay_milliseconds(2);

   gu2_vga_extcrtc(0x3F, 1);
}

void
gu2_gfx_to_vga(int vga_mode)
{
   char sequencer;

   gu2_vga_extcrtc(0x40, vga_mode);

   /* clear the display blanking bit */
   gfx_outb(MDC_SEQUENCER_INDEX, MDC_SEQUENCER_CLK_MODE);
   sequencer = gfx_inb(MDC_SEQUENCER_DATA);
   sequencer &= ~MDC_CLK_MODE_SCREEN_OFF;
   sequencer |= 1;
   gfx_outb(MDC_SEQUENCER_DATA, sequencer);

   gfx_delay_milliseconds(1);

   /*restart the sequencer */
   gfx_outw(0x3C4, 0x300);

   /* turn on the attribute controler */
   (void) gfx_inb(0x3DA);
   gfx_outb(0x3C0, 0x20);
   (void) gfx_inb(0x3DA);

   gu2_vga_extcrtc(0x3F, 0);
}

/*-----------------------------------------------------------------------------
 * gfx_vga_seq_reset
 *
 * This routine enables or disables SoftVGA.  It is used to make SoftVGA 
 * "be quiet" and not interfere with any of the direct hardware access from 
 * Durango.  For VSA1, the sequencer is reset to stop text redraws.  VSA2 may 
 * provide a better way to have SoftVGA sit in the background.
 *-----------------------------------------------------------------------------
 */
int
gu2_vga_seq_reset(int reset)
{
   gfx_outb(0x3C4, 0);
   gfx_outb(0x3C5, (unsigned char)(reset ? 0x00 : 0x03));
   return (GFX_STATUS_OK);
}

/*-----------------------------------------------------------------------------
 * gfx_vga_save
 *
 * This routine saves the state of the VGA registers into the specified 
 * structure.  Flags indicate what portions of the register state need to 
 * be saved.
 *-----------------------------------------------------------------------------
 */
int
gu2_vga_save(gfx_vga_struct * vga, int flags)
{
   int i;
   unsigned short crtcindex, crtcdata;

   crtcindex = (gfx_inb(0x3CC) & 0x01) ? 0x3D4 : 0x3B4;
   crtcdata = crtcindex + 1;

   /* CHECK MISCELLANEOUS OUTPUT FLAG */

   if (flags & GU2_VGA_FLAG_MISC_OUTPUT) {
      /* SAVE MISCCELLANEOUS OUTPUT REGISTER */

      vga->miscOutput = gfx_inb(0x3CC);
   }

   /* CHECK SEQ */

   if (flags & GU2_VGA_FLAG_SEQ) {
      /* SAVE STANDARD CRTC REGISTERS */

      for (i = 1; i < GU2_SEQ_REGS; i++) {
	 gfx_outb(0x3C4, (unsigned char)i);
	 SEQregs[i] = gfx_inb(0x3C5);
      }
   }

   /* CHECK STANDARD CRTC FLAG */

   if (flags & GU2_VGA_FLAG_STD_CRTC) {
      /* SAVE STANDARD CRTC REGISTERS */

      for (i = 0; i < GU2_STD_CRTC_REGS; i++) {
	 gfx_outb(crtcindex, (unsigned char)i);
	 vga->stdCRTCregs[i] = gfx_inb(crtcdata);
      }
   }

   /* CHECK GDC */

   if (flags & GU2_VGA_FLAG_GDC) {
      /* SAVE STANDARD CRTC REGISTERS */

      for (i = 0; i < GU2_GDC_REGS; i++) {
	 gfx_outb(0x3CE, (unsigned char)i);
	 GDCregs[i] = gfx_inb(0x3CF);
      }
   }

   /* CHECK EXTENDED CRTC FLAG */

   if (flags & GU2_VGA_FLAG_EXT_CRTC) {
      /* SAVE EXTENDED CRTC REGISTERS */

      for (i = 0; i < GU2_EXT_CRTC_REGS; i++) {
	 gfx_outb(crtcindex, (unsigned char)(0x40 + i));
	 vga->extCRTCregs[i] = gfx_inb(crtcdata);
      }
   }

   if (flags & GU2_VGA_FLAG_PALETTE) {
      /* SAVE PALETTE DATA */

      for (i = 0; i < 0x100; i++) {
	 gfx_outb(0x3C7, i);
	 palette[i] = gfx_inb(0x3C9);
      }
   }

   if (flags & GU2_VGA_FLAG_ATTR) {
      /* SAVE Attribute  DATA */

      for (i = 0; i < 21; i++) {
	 gfx_inb(0x3DA);
	 gfx_outb(0x3C0, i);
	 ATTRregs[i] = gfx_inb(0x3C1);
      }
   }
   /* save the VGA data */
   gu2_vga_font_data(0);
   return (0);
}

/*-----------------------------------------------------------------------------
 * gfx_vga_clear_extended
 *
 * This routine clears the extended SoftVGA register values to have SoftVGA
 * behave like standard VGA. 
 *-----------------------------------------------------------------------------
 */
void
gu2_vga_clear_extended(void)
{
   int i;
   unsigned short crtcindex, crtcdata;

   crtcindex = (gfx_inb(0x3CC) & 0x01) ? 0x3D4 : 0x3B4;
   crtcdata = crtcindex + 1;

   gfx_outb(crtcindex, 0x30);
   gfx_outb(crtcdata, 0x57);
   gfx_outb(crtcdata, 0x4C);
   for (i = 0x41; i <= 0x4F; i++) {
      gfx_outb(crtcindex, (unsigned char)i);
      gfx_outb(crtcdata, 0);
   }
   gfx_outb(crtcindex, 0x30);
   gfx_outb(crtcdata, 0x00);
}

void
gu2_vga_extcrtc(char offset, int reset)
{
   unsigned short crtcindex, crtcdata;

   crtcindex = (gfx_inb(0x3CC) & 0x01) ? 0x3D4 : 0x3B4;
   crtcdata = crtcindex + 1;

   /* UNLOCK EXTENDED CRTC REGISTERS */

   gfx_outb(crtcindex, 0x30);
   gfx_outb(crtcdata, 0x57);
   gfx_outb(crtcdata, 0x4C);

   /* RESTORE EXTENDED CRTC REGISTERS */

   gfx_outb(crtcindex, offset);
   gfx_outb(crtcdata, reset);

#if 0
   /* LOCK EXTENDED CRTC REGISTERS */

   gfx_outb(crtcindex, 0x30);
   gfx_outb(crtcdata, 0x00);
#endif
}

/*-----------------------------------------------------------------------------
 * gfx_vga_restore
 *
 * This routine restores the state of the VGA registers from the specified
 * structure.  Flags indicate what portions of the register state need to 
 * be saved.
 *-----------------------------------------------------------------------------
 */
int
gu2_vga_restore(gfx_vga_struct * vga, int flags)
{
   int i;
   unsigned short crtcindex, crtcdata;

   crtcindex = (gfx_inb(0x3CC) & 0x01) ? 0x3D4 : 0x3B4;
   crtcdata = crtcindex + 1;

   /* CHECK MISCELLANEOUS OUTPUT FLAG */

   if (flags & GU2_VGA_FLAG_MISC_OUTPUT) {
      /* RESTORE MISCELLANEOUS OUTPUT REGISTER VALUE */

      gfx_outb(0x3C2, vga->miscOutput);
   }

   /* CHECK SEQ */

   if (flags & GU2_VGA_FLAG_SEQ) {
      /* RESTORE STANDARD CRTC REGISTERS */

      for (i = 1; i < GU2_SEQ_REGS; i++) {
	 gfx_outb(0x3C4, (unsigned char)i);
	 gfx_outb(0x3C5, SEQregs[i]);
      }
   }

   /* CHECK STANDARD CRTC FLAG */

   if (flags & GU2_VGA_FLAG_STD_CRTC) {
      /* UNLOCK STANDARD CRTC REGISTERS */

      gfx_outb(crtcindex, 0x11);
      gfx_outb(crtcdata, 0);

      /* RESTORE STANDARD CRTC REGISTERS */

      for (i = 0; i < GU2_STD_CRTC_REGS; i++) {
	 gfx_outb(crtcindex, (unsigned char)i);
	 gfx_outb(crtcdata, vga->stdCRTCregs[i]);
      }
   }

   /* CHECK GDC */

   if (flags & GU2_VGA_FLAG_GDC) {
      /* SAVE STANDARD CRTC REGISTERS */

      for (i = 0; i < GU2_GDC_REGS; i++) {
	 gfx_outb(0x3CE, (unsigned char)i);
	 gfx_outb(0x3CF, GDCregs[i]);
      }
   }

   /* CHECK EXTENDED CRTC FLAG */

   if (flags & GU2_VGA_FLAG_EXT_CRTC) {
      /* UNLOCK EXTENDED CRTC REGISTERS */

      gfx_outb(crtcindex, 0x30);
      gfx_outb(crtcdata, 0x57);
      gfx_outb(crtcdata, 0x4C);

      /* RESTORE EXTENDED CRTC REGISTERS */

      for (i = 1; i < GU2_EXT_CRTC_REGS; i++) {
	 gfx_outb(crtcindex, (unsigned char)(0x40 + i));
	 gfx_outb(crtcdata, vga->extCRTCregs[i]);
      }

      /* LOCK EXTENDED CRTC REGISTERS */

      gfx_outb(crtcindex, 0x30);
      gfx_outb(crtcdata, 0x00);

      /* CHECK IF DIRECT FRAME BUFFER MODE (VESA MODE) */

      if (vga->extCRTCregs[0x03] & 1) {
	 /* SET BORDER COLOR TO BLACK */
	 /* This really should be another thing saved/restored, but */
	 /* Durango currently doesn't do the attr controller registers. */

	 gfx_inb(0x3BA);		/* Reset flip-flop */
	 gfx_inb(0x3DA);
	 gfx_outb(0x3C0, 0x11);
	 gfx_outb(0x3C0, 0x00);
      }
   }

   if (flags & GU2_VGA_FLAG_PALETTE) {
      /* RESTORE PALETTE DATA */

      for (i = 0; i < 0x100; i++) {
	 gfx_outb(0x3C8, i);
	 gfx_outb(0x3C9, palette[i]);
      }
   }

   if (flags & GU2_VGA_FLAG_ATTR) {
      /* RESTORE Attribute  DATA */

      for (i = 0; i < 21; i++) {
	 gfx_inb(0x3DA);
	 gfx_outb(0x3C0, i);
	 gfx_outb(0x3C0, ATTRregs[i]);
      }
      /* SAVE Attribute  DATA */

      for (i = 0; i < 21; i++) {
	 gfx_inb(0x3DA);
	 gfx_outb(0x3C0, i);
      }
   }

   /* restore the VGA data */
   gu2_vga_font_data(1);

   return (0);
}

/* END OF FILE */
