#include <stdio.h>
#include "libwr2rf/board.h"
#include "regs.h"

struct dac_reg {
  uint8_t reg;
  uint8_t val;
};

static struct dac_reg dac_config[] =
  {
   /* 4 wires, MSB first, no reset.  */
   { DAC_REG_SPI_CONTROL, 0},

   /* Unsigned.  */
//   { DAC_REG_DATA_CONTROL, 0x80},

   /* signed */
   { DAC_REG_DATA_CONTROL, 0x00},

   /* No power down.  */
   { DAC_REG_POWER_DOWN, 0},

   /* Init value.  */
   { DAC_REG_SETUP_HOLD, 0},
   { DAC_REG_TIMING_ADJ, 0},

   /* Normal mode.  */
   { DAC_REG_MIX_MODE, 0},

   /* DAC1 full scale adjustment: 20.0mA.  */
   { DAC_REG_DAC1_FSC_HI, 0x02},
   { DAC_REG_DAC1_FSC_LO, 0x00},

   /* AUXDAC1: 0.0mA, AUX1P active, source current  */
   { DAC_REG_AUXDAC1_HI, 0x00},
   { DAC_REG_AUXDAC1_LO, 0x00},

   /* DAC2 full scale adjustment: 20.0mA.  */
   { DAC_REG_DAC2_FSC_HI, 0x02},
   { DAC_REG_DAC2_FSC_LO, 0x00},

   /* AUXDAC2: 0.0mA, AUX2P active, source current  */
   { DAC_REG_AUXDAC2_HI, 0x00},
   { DAC_REG_AUXDAC2_LO, 0x00},
};

void libwr2rf_dac_init(struct libwr2rf_dev *dev, unsigned ch)
{
  unsigned i;

  /* Assume SPI is already initialized.  */

  printf ("dac %u version: 0x%02x\n",
          ch, libwr2rf_dac_read(dev, ch, DAC_REG_VERSION));

  for (i = 0; i < sizeof(dac_config)/sizeof(dac_config[0]); i++)
    libwr2rf_dac_write(dev, ch, dac_config[i].reg, dac_config[i].val);
}

void libwr2rf_dac_set_setup_hold(struct libwr2rf_dev *dev, unsigned ch, unsigned set, unsigned hld)
{
  libwr2rf_dac_write(dev, ch, DAC_REG_SETUP_HOLD, (set << 4) | hld);
}

void libwr2rf_dac_set_timing_delay(struct libwr2rf_dev *dev, unsigned ch, unsigned v)
{
  libwr2rf_dac_write(dev, ch, DAC_REG_TIMING_ADJ, v);
}

unsigned libwr2rf_dac_get_timing_delay(struct libwr2rf_dev *dev, unsigned ch)
{
  return libwr2rf_dac_read(dev, ch, DAC_REG_TIMING_ADJ);
}

unsigned libwr2rf_dac_get_seek(struct libwr2rf_dev *dev, unsigned ch)
{
  return libwr2rf_dac_read(dev, ch, DAC_REG_SEEK) & 1;
}

int libwr2rf_dac_port_timing(struct libwr2rf_dev *dev, unsigned ch)
{
  struct port_res {
    unsigned seek;
    unsigned set;
    unsigned hld;
  };
  struct port_res results[32];
  unsigned seek0;
  unsigned seek1;
  unsigned hld;
  unsigned set;
  unsigned i;
  unsigned smp;
  unsigned best;

  /* See AD9783 data sheet rev C, p 26.  */

  /* 1.  Set the values of SMP, SET and HLD to 0.  Read and record
     the value of the SEEK bit.  */
  libwr2rf_dac_set_setup_hold(dev, ch, 0, 0);
  libwr2rf_dac_set_timing_delay(dev, ch, 0);
  seek0 = libwr2rf_dac_get_seek(dev, ch);

  /* 2.  With SMP and SET set to 0, increment the HLD value until
     the seek bit toggles, and then record the HLD value.  This
     measures the hold time as shown in figure 57.  */  
  hld = 15;
  for (i = 0; i < 16; i++) {
      libwr2rf_dac_set_setup_hold(dev, ch, 0, i);
      seek1 = libwr2rf_dac_get_seek(dev, ch);
      if (seek1 != seek0) {
          hld = i;
          break;
      }
  }

  /* 3.  With SMP and HLD set to 0, increment the SET value until
     the SEEK bit toggles, and then record the SET value.  This
     measures the setup time as shown in figure 57.  */
  set = 15;
  for (i = 0; i < 16; i++) {
      libwr2rf_dac_set_setup_hold(dev, ch, i, 0);
      seek1 = libwr2rf_dac_get_seek(dev, ch);
      if (seek1 != seek0) {
          set = i;
          break;
      }
  }
  printf("AD9783 SET=%d HLD=%d\n", hld, set);

  /* Building the Array.  */
  for (smp = 0; smp < 32; smp++)
    {
      /* 4.  Set the value of SET and HLD to 0.  Increment the value of SMP
             and record the value of the SEEK bit.  */
      libwr2rf_dac_set_setup_hold(dev, ch, 0, 0);
      libwr2rf_dac_set_timing_delay(dev, ch, smp);
      seek0 = libwr2rf_dac_get_seek(dev, ch);

      /* 5a.  Increment the HLD until the SEEK bit tooles, and then record
         the HLD value.  */
      hld = 15;
      for (i = 0; i < 16; i++) {
        libwr2rf_dac_set_setup_hold(dev, ch, 0, i);
        seek1 = libwr2rf_dac_get_seek(dev, ch);
        if (seek1 != seek0) {
          hld = i;
          break;
        }
      }

      /* 5b.  Set HLD to 0 and increment SET until the SEEK bit
              toggles, and then record the SET value.  */
      set = 15;
      for (i = 0; i < 16; i++) {
        libwr2rf_dac_set_setup_hold(dev, ch, i, 0);
        seek1 = libwr2rf_dac_get_seek(dev, ch);
        if (seek1 != seek0) {
          set = i;
          break;
        }
      }

      results[smp].seek = seek0;
      results[smp].set = set;
      results[smp].hld = hld;
    }

  printf("dac %u: smp  set  hld seek\n", ch);
  for (smp = 0; smp < 32; smp++)
      printf("        %2u   %2u   %2u  %2u\n",
             smp, results[smp].set, results[smp].hld, results[smp].seek);

#if 0  
  unsigned lo;
  unsigned hi;

  /* Determining the SMP value.  */
  /* 1.  Look for the SMP value that corresponds to the 0-to-1 transition
         of the SEEK bit in the table.  */
  for (lo = 0; lo < 31; lo++) {
      if (results[lo].seek == 0 && results[lo+1].seek == 1) {
          lo++;
          break;
      }
  }

  if (lo >= 31) {
    printf ("dac %u: no 0-to-1 transition\n", ch);
    return -1;
  }

  /* 2.  Look for the SMP value that corresponds to the 1-to-0 transition
         of the SEEK bit in the table.  */
  for (hi = lo + 1; hi < 32; hi++) {
      if (results[hi - 1].seek == 1 && results[hi].seek == 0)
          break;
  }
  if (hi == 32) {
    printf ("dac %u: no 1-to-0 transition\n", ch);
  }
  /* 3.  The same two values found in Step 1 and Step 2 indicate the
         valid sampling window.  */
  printf ("dac %u: sampling window: %u-%u\n", ch, lo, hi);

  /* 4.  The optimal SMP value in the valid sampling window is where
         the following two conditions are true: SET < HDL and |HLD - SET| is
         the smallest value.  */
  {
    unsigned diff = 0xff;

    best = 0xff;

    for (i = lo + 1; i < hi - 1; i++)
      {
        if (results[i].set < results[i].hld)
          {
            unsigned d = results[i].hld - results[i].set;
            if (d < diff)
              best = i;
          }
      }
    if (best > 32) {
      printf ("dac %u: no best sampling window\n", ch);
      return -1;
    }
  }

  /* After programming the calculated value of SMP (referred to as SMPoptimal),
     the configuration must be tested to verify that there is sufficient
     timing margin.  This can be accomplished by ensuring that the SEEK bit
     reads back as a 1 for SMP values equal to SMPoptimal+1 and SMPoptimal-1.
  */
  if (best == 0 || results[best - 1].seek != 1) {
    printf ("dac %u: bad seek value for SMPopt-1 (%u)\n", ch, best - 1);
    return -1;
  }
  if (best == 31 || results[best + 1].seek != 1) {
    printf ("dac %u: bad seek value for SMPopt+1 (%u)\n", ch, best + 1);
    return -1;
  }

  /* Also, it can be noted that the sum of SET and HLD must be a minimum of
     8.  If the sum is lower than this, you must check for excessive jitter
     on the clock input line and check that the frequency of the clock
     input does not exceed the data sheet maximum of 500Mhz.  */
  if (results[best].hld + results[best].set < 8) {
    printf ("dac %u: excessive jitter\n", ch);
    return -1;
  }

#else
  unsigned min_diff = 0x10;
  int      diff;
  unsigned sum;
  unsigned max_sum  = 0x0;
  
  best = 32;
  for (i = 1; i < 31; i++) {
      if (results[i].seek == 0) {// this is inverted, to avoid two acceptable windows
          diff = results[i].hld - results[i].set;
          sum  = results[i].hld + results[i].set;
          if (results[i].set < results[i].hld) {
              if ( (results[i].set + results[i].hld) > 8 ) {
                  printf ("set=%02u hld=%02u diff=%02d, sum=%02u\n", results[i].set, results[i].hld, diff, sum);
                  if ((diff < min_diff) && (sum >= max_sum)) {
                      min_diff = diff;
                      max_sum  = sum;
                      best     = i;
                  }
              }
          }        
      }          
  }

  if (best == 32) {
      printf ("ERROR: dac %u: no best sampling window\n", ch);
  } else {
      printf ("dac %u: best sampling: %u\n", ch, best);

      libwr2rf_dac_set_timing_delay(dev, ch, best);
  }

#endif

  
  return 0;
}


int libwr2rf_dac_validate_timing(struct libwr2rf_dev *dev, unsigned ch)
{
    unsigned smp_best = libwr2rf_dac_get_timing_delay(dev, ch);
    unsigned smp_bestm1;
    unsigned smp_bestp1;
    unsigned seekm1 = 0;
    unsigned seekp1 = 0;
    unsigned seek   = 0;

    libwr2rf_dac_set_setup_hold(dev, ch, 0, 0);
    if (smp_best == 0)
        printf("WARNING: SMP=%x, cannot determine lower timing margin\n", smp_best);
    else
        smp_bestm1 = smp_best - 1;
    
    if (smp_best != 0) {
        libwr2rf_dac_set_timing_delay(dev, ch, smp_bestm1);
        seekm1 = libwr2rf_dac_get_seek(dev, ch);
    }

    if (smp_best == 31)
        printf("WARNING: SMP=%x, cannot determine upper timing margin\n", smp_best);
    else
        smp_bestp1 = smp_best + 1;

    if (smp_best != 31) {
        libwr2rf_dac_set_timing_delay(dev, ch, smp_bestp1);
        seekp1 = libwr2rf_dac_get_seek(dev, ch);
    }

    libwr2rf_dac_set_timing_delay(dev, ch, smp_best);
    seek = libwr2rf_dac_get_seek(dev, ch);    
    if (seek != 1)
        printf("WARNING DAC: AD9783 RF Channel=%u timing is BROKEN\n", ch);

    if (seekm1 == 1 && seekp1 == 1 && seek == 1) {
        printf("DAC: AD9783 RF Channel=%x validated \n", ch);
        return 0;
    }

    printf("DAC: AD9783 RF Channel=%x NOT validated: seekm1=%x seek=%x seekp1=%x\n", ch, seekm1, seek, seekp1);
    return -1;
}
