/*  SVGATextMode -- An SVGA textmode manipulation/enhancement tool
 *
 *  Copyright (C) 1995  Koen Gadeyne
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it 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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/***
 *** parse_clock.c
 *** clock configuration parsing code for SVGATextMode
 *** (c) 1995 Koen Gadeyne (kmg@barco.be)
 ***/


/*
 * If defined, CHECK_NUMCLOCKS will check if the number of clocks in the Clocks line 
 * corresponds to the number that SVGATextMode expects. This is more of a hassle than an advantage,,,
 */


#define CHECK_NUMCLOCKS

#include "misc.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "confdefs.h"
#include "setclock.h"
#include "clockchip.h"
#include "std_clock.h"
#include "configfile.h"
#include "messages.h"
#include "parse_clock.h"


/*
 * Scan the config file for clock programming method, and store result in return struct.
 */ 

t_clockdef* parse_clock(FILE* configfile, int chipset, t_clockdef* clockdef)
{
  const int str_allowedclkchips[NUM_CHIPSETS] = CLOCKCHIPS_ALLOWED;
  const char *str_clkchips[NUM_CLKCHIPS] = CLKCHIP_STRINGS;
  const float clock_limits[NUM_CHIPSETS] = CLOCK_LIMITS;
  int clocksline;
  char* arg_str;
  
  
  /* default values */
  clockdef->type = CK_NONE;
  clockdef->num_clocks = 0;
  clockdef->clocks[0] = -1;
  clockdef->clockchiptype = CLKCHIP_NONE;
  clockdef->ck_prog_path[0]  = '\0';
  clockdef->maxclock = 45.00;
  clockdef->MClk = MCLK_NOT_DEFINED;

 /*
  * get external clock program ; ignore the '[textclock]' field that is used in XFREE. It is of no use here.
  */

  if(GetPathOption(configfile, "ClockProg", LABEL_OPTIONAL+LABEL_FROMSTART, clockdef->ck_prog_path))
     clockdef->type |= CK_CLOCKPROG;
    
 /*
  * get special clock chip: this must not coincide with a ClockProg
  */

  if ((arg_str = findlabel(configfile, "ClockChip", LABEL_OPTIONAL+LABEL_FROMSTART)))
  {
    strtok(arg_str," ");  /* loose the 'ClockChip' keyword */
    if (clockdef->type & CK_CLOCKPROG)
      PERROR(("You cannot use BOTH an external clock program AND a clock chip. Make up your mind!"));
    if (!(arg_str=strtok(NULL," "))) PERROR(("'ClockChip': clock chip type not defined"));
    clockdef->clockchiptype = findoption(arg_str, str_clkchips, NUM_CLKCHIPS, "Clock Chip");
    /* check if clockchip allowed for selected chip set */
    PDEBUG(("Allowable-clockchip mask: 0x%08x", str_allowedclkchips[chipset]));
    if ( !(str_allowedclkchips[chipset] & (1<<clockdef->clockchiptype)))
      PERROR(("ClockChip '%s' not allowed for this chipset.", str_clkchips[clockdef->clockchiptype]));
    clockdef->type |= CK_CLOCKCHIP;
    /* now fill the clocks structure appropriately: 3 clocks: 25, 28 and the programmable clock */
    clockdef->num_clocks=3;
    clockdef->clocks[0]=25.175; /* 2 VGA standard clocks */
    clockdef->clocks[1]=28.322;
    clockdef->clocks[2]=CKVAL_PROGRAMMABLE; /* the programmable clock */
  }

 /*
  * get pixel clocks (multiple lines possible) (only when needed).
  */

  clocksline = ( (clockdef->type & CK_CLOCKPROG) || ( (!(clockdef->type & CK_CLOCKCHIP)) && (chipset!=CS_CIRRUS)) );
  
  if (clocksline)
  {
    PDEBUG(("Not Cirrus, not clockchip, or clockprog defined: 'Clocks' line needed!"));
    arg_str = findlabel(configfile, "Clocks", LABEL_REQUIRED+LABEL_FROMSTART);
    strtok(arg_str," ");  /* dump the 'Clocks' keyword. We already got that */
    clockdef->type|=CK_SET_OF_CLOCKS;
    clockdef->num_clocks = 0;
    do
    {
      while ((arg_str=strtok(NULL," ")))
        clockdef->clocks[clockdef->num_clocks++] = getfloat(arg_str, "'Clocks' definition: clock frequency in configuration file", MIN_CLOCK, MAX_CLOCK);
      if (clockdef->num_clocks >MAX_CLOCKS)
         PWARNING(("Too many clocks (%d, max=%d) defined in config file '%s'",clockdef->num_clocks,MAX_CLOCKS, ConfigFile));
      if (clockdef->num_clocks) PDEBUG(("Clocks: found %d clocks (until now)",clockdef->num_clocks));
    }
    while ((arg_str=strtok(findlabel(configfile, "Clocks", LABEL_OPTIONAL+LABEL_FROMCURRENT)," ")));
    PDEBUG(("Clocks: found %d clocks in total",clockdef->num_clocks));

#ifdef CHECK_NUMCLOCKS  
   /*
    * check for correct amount of clocks, if possible for the specified chip set. This could be incorrect...
    */

    if ((chipset == CS_VGA) && (clockdef->num_clocks > 4))
      PERROR(("Generic VGA chipsets can have no more than 4 clocks (and not %d).", clockdef->num_clocks));
    if ((chipset == CS_PVGA1) && (clockdef->num_clocks != 8) && (clockdef->num_clocks != 4))
      PERROR(("PVGA1 chipset must have 4 or 8 clocks in 'clocks' line"));
    if (((chipset == CS_WDC90C0X) || (chipset == CS_WDC90C1X) || (chipset == CS_WDC90C2X)) && (clockdef->num_clocks != 9))
      PERROR(("WDC90C0X, 1X and 2X chipsets have 8 pixel clocks PLUS the MClock."));
    if ((chipset == CS_WDC90C3X) && (clockdef->num_clocks != 17))
      PERROR(("WDC90C3X chipsets have 16 pixel clocks PLUS the MClock."));
    if ((chipset==CS_ATI) && (clockdef->num_clocks != 64))
      PWARNING(("ATI chipsets (VGA Wonder V5 and above) normally have 64 clocks."));
    if ((chipset==CS_ATIMACH32) && (clockdef->num_clocks != 32))
      PWARNING(("ATI MACH normally have 32 clocks."));
#endif         
  }
  else PDEBUG(("No 'Clocks' line required for this setup"));

  if ((arg_str = findlabel(configfile, "MClk", LABEL_OPTIONAL+LABEL_FROMSTART)))
  {
    strtok(arg_str," ");
    if (clockdef->type & CK_CLOCKPROG)
      PERROR(("Setting MClk is not possible when using a ClockProg"));
    if (!(arg_str=strtok(NULL," "))) PERROR(("'MClk': clock value not specified"));
    clockdef->MClk = getfloat(arg_str, "'MClk' definition: memory clock frequency in configuration file", MIN_MCLK, MAX_MCLK);
    PDEBUG(("MClk will be %3.2f MHz", clockdef->MClk));
  }

/*
 * Get max DAC speed. Default depends on chipset.
 */
 
  clockdef->maxclock=clock_limits[chipset];   /* default */
  
  if ((arg_str = findlabel(configfile, "DacSpeed", LABEL_OPTIONAL+LABEL_FROMSTART)))
  {
    strtok(arg_str," ");  /* loose the 'DacSpeed' keyword */
    if (!(arg_str=strtok(NULL," "))) PERROR(("'DacSpeed': value not defined"));
    clockdef->maxclock = getfloat(arg_str, "Maximum DAC speed", 1, 500);
    PDEBUG(("Setting Max Clock (dacspeed) to %3.2f MHz", clockdef->maxclock));
  }

 /*
  * Now see if any clock programming method was defined. If not, bail out.
  */
  
  if ((!clocksline) && (chipset!=CS_CIRRUS) && (clockdef->type!=CK_CLOCKCHIP))
    PERROR(("You must define either a 'ClockProg', a 'ClockChip' or a 'Clocks' line in the config file!"));

  return(clockdef);
}

