/* **************************************************************************
 * 
 * Some code to mess around with the Video Blaster's 82c9001a windowing 
 * chip.  Looks like fun.
 *
 * Someday this will be moved around and made to work with v4l and the i2c
 * code will be abstracted to work with the i2c module already in the kernel.
 *
 * Under FSF GNU GPLv2.  (c) 1998 Adam Fritzler (afritz@delphid.ml.org)
 *
 * Last modified somewhere before sunrise on 23 Dec 1998ad.
 *
 * **************************************************************************/

#include <stdio.h>
#include <asm/io.h>


/*****************************************************************************/

/*
 * CT82c89001a-specific stuff.  mostly register read/writes and things...
 *
 */

#define CT82_ADDR_IDX 0x2ad6
#define CT82_ADDR_DATA 0x2ad7 /* CT82_ADDR_IDX+1 */

/*
 * ct82_read( reg )
 *
 *  return value of register reg on 82c9001a.
 *
 */
unsigned char ct82_read(unsigned char reg)
{
  outb(reg, CT82_ADDR_IDX);
  return inb(CT82_ADDR_DATA);
}

/*
 * ct82_write( reg, data )
 *
 *  write byte data to register reg on 82c9001a.
 *
 */
void ct82_write(unsigned char reg, unsigned char data)
{
  outb(reg, CT82_ADDR_IDX);
  outb(data, CT82_ADDR_DATA);
}

/*****************************************************************************/

/*
 * 82c9001a-specific stuff that's needed for i2c mucking...
 *
 */

/*
 * ct82_i2c_regwrite( clock, data )
 *
 * Set the lines high or low...
 *
 *   clock   data    result
 *     0       0     pull SCL and SDA low
 *     0       1     pull SCL low, SDA high
 *     1       0     pull SCL high, SDA low
 *     1       1     pull SCL and SDA high
 *
 * Keep in mind that pulling SDA anywhere while SCL is high is a Bad Thing.
 *
 */
void ct82_i2c_regwrite(unsigned char x, unsigned char y)
{
  if (y > 1)
    return;
#if 0
  /* code before i killed it -- derived from Bernhard's code */
  ct82_write(0x18, (ct82_read(0x18) & (~(1 << y))) | ((x & 1) << y));
#endif

  /* insert rethought code here */

#if 0
  /* I wasn't even THINKING correctly for this stuff... */

  if (x == 0) /* set clock */
    {
      if (x == 0) /* set clock to zero (low) */
	{
	  ct82_write(0x18, (ct82_read(0x18) & 0x01)); 
	  printf("set clock low\n");
	}
      else /* set clock to on (high) */
	{
	  ct82_write(0x18, (ct82_read(0x18) | 0x01));
	  printf("set clock high\n");
	}
    }
  if (y == 1) /* set data */
    {
      if (x == 0) /* set data low */
	{
	  ct82_write(0x18, (ct82_read(0x18) & 0x02));
	  printf("set data low\n");
	}
      else /* set data high */
	{
	  ct82_write(0x18, (ct82_read(0x18) | 0x02));
	  printf("set data high\n");
	}
    }
#endif

}

/*
 * ct82_i2c_regread()
 *
 * Reads the "i2c Read Back" pin on the 82c9001a.  Bit three on RX18.
 *
 */
unsigned char ct82_i2c_regread()
{
  return ((ct82_read(0x18) >> 2) & 1);
}

/*
 * ct82_i2c_ack()
 *
 * Uhm...lets wait till I know what I'm doing to explain this one...
 *
 */
unsigned char ct82_i2c_ack(void)
{
  /* doesn't match kernel! */
  unsigned char res;
  ct82_i2c_regwrite(0, 1); /* 1 1 */   /* set clock low, data high */
  ct82_i2c_regwrite(1, 1); /* 1 0 */   /* move clock high */
  res = ct82_i2c_regread(2);  /* see if clock really moved high */
  ct82_i2c_regwrite(0, 1); /* 0 0 */  /* move clock low */
  return res;
}

/*****************************************************************************/

/*
 * i2c Buss Arbitration....
 *
 * Before data is sent, the START bit must be exectuted.  The STOP bit
 * must end every transmittion.
 *
 */

/*
 * ct82_i2c_start()
 * 
 * "Transmits" an i2c "START bit".  This is formed by moving the data line
 * down while the clock is high.
 *
 *
 *
 * HIGH  __________          
 *                 |
 *  LOW            |___________   SDA (data)
 *
 *
 * HIGH        __________ 
 *            |          |
 *  LOW  _____|          |_____   SCL (clock)
 *
 *
 */
void ct82_i2c_start(void)
{
  ct82_i2c_regwrite(0, 1); /* 1 1 */  /* set clock low, data high */
  ct82_i2c_regwrite(1, 1); /* 1 0 */  /* move clock up */
  ct82_i2c_regwrite(1, 0); /* 0 1 */  /* move data low while clock is up */
  ct82_i2c_regwrite(0, 0); /* set both low (to start data transfer) */
}

/*
 * ct82_i2c_stop()
 *
 * Transmits an i2c "STOP bit".  This is formed by moving the data high
 * while the clock line is up.
 *
 *
 * HIGH             _____
 *                 |
 *  LOW  __________|        SDA (data)
 *
 *
 * HIGH        __________ 
 *            |     
 *  LOW  _____|             SCL (clock)
 *
 *
 */
void ct82_i2c_stop(void)
{
  ct82_i2c_regwrite(0, 0); /* 0 1 */ /* set both low */
  ct82_i2c_regwrite(1, 0); /* set clock high */
  ct82_i2c_regwrite(1, 1); /* move data high with clock up */
}

/*****************************************************************************/

/*
 * i2c transmittion...
 *
 * "Data is transfered when the clock line is LOW. When the clock is HIGH, 
 *  the data line must be stable."
 *
 */

/*
 * ct82_i2c_one()
 *
 * Transmit a ONE on the i2c bus.
 *
 */
void ct82_i2c_one(void)
{
  /*
   *
   * Keep SDA high while strobing SCL low-high-low....
   *
   * HIGH  ________________
   *                          SDA (data)
   *  LOW                    
   *
   *
   * HIGH        ____
   *            |    |        SCL (clock)
   *  LOW  _____|    |_____ 
   *
   *
   */
  ct82_i2c_regwrite(0, 1);
  ct82_i2c_regwrite(1, 1);
  ct82_i2c_regwrite(0, 1);
}

/*
 * ct82_i2c_zero()
 *
 * Transmit a ZERO on the i2c bus.
 *
 */
void ct82_i2c_zero(void)
{
  /*
   *
   * Keep SDA low while strobing SCL low-high-low....
   *
   * HIGH
   * 
   *  LOW  ________________   SDA (data)
   *
   *
   * HIGH        ____
   *            |    |
   *  LOW  _____|    |_____   SCL (clock)
   *
   *
   */
  ct82_i2c_regwrite(0, 0);
  ct82_i2c_regwrite(1, 0);
  ct82_i2c_regwrite(0, 0);
}

/*
 * Send merely a byte of data. (i say merely like its easy or something)
 *
 */
unsigned char ct82_i2c_sendbyte(unsigned char x, unsigned char y)
{
  char j;
  unsigned char i;
  
#if 0
  /* this is bernhard's code...ya, it works when i started */
  if (!y)
    ct82_i2c_start();
  y = x;  
  for (i = 0; i < 8; i++)
    {
      ct82_i2c_regwrite(x >> 7, 1);
      ct82_i2c_regwrite(1, 0);
      ct82_i2c_regwrite(0, 0);
      x <<= 1;
    }
#endif
  
#if 1
  /* this is derived from Gerd's i2c module... */
  y = x;
  ct82_i2c_regwrite(0, 0);
  for (j=7; j >= 0; j--)
    (x & (1<<j)) ? ct82_i2c_one() : ct82_i2c_zero();
#endif
  
  i = ct82_i2c_ack();
  printf("\t0x%02x: %c\n", y, i?'-':'+');
  return i;
}

/*
 * c82_i2c_write( mad, sad, data )
 *
 * Writes out a full i2c transmission.  That means with MAD, SAD, and data.
 *
 * MAD: Module ADdress
 *      This is the module address (aka, slave address).  Bascially a
 *      unique ID that each chip on the buss gets.  This is the recieve
 *      MAD.  When a chip wants to transmit, it comes from MAD+1.  Therefore,
 *      target MADs only occur on even numbers, source MADs on odds.  
 * 
 * SAD: Sub-ADdress
 *      The SAD is the register number on the MAD that you want to twiddle.
 *      See the databook for each particular device you want to control.
 *
 * Data: you know what data is.  Here, though, only one byte.  The Philips
 *       spec says you can bang as much as you want, however Gerd's module
 *       only allows for one byte.  I'm guessing its plenty.
 *
 */

unsigned char ct82_i2c_write(unsigned char mad, 
			     unsigned char sad, 
			     unsigned char data)
{

  /* MAD goes first... */
  if (ct82_i2c_sendbyte(mad, 0) != 0)
    {
      ct82_i2c_stop(); /* error: send STOP and abort */
      return 0;
    }

  /* then the SAD... */
  if (ct82_i2c_sendbyte(sad, 1) != 0)
    {
      ct82_i2c_stop(); /* error: send STOP and abort */
      return 0;
    }

  /* then the data... */
  if (ct82_i2c_sendbyte(data, 2) != 0)
    {
      ct82_i2c_stop(); /* error: send STOP and abort */
      return 0;
    }
  else
    {
      /* successful */
      ct82_i2c_stop(); /* send STOP */
      return 1;
    }
}

/*
 * ct82_i2c_read() 
 *
 * Reads a byte off the bus.  Too early for this one yet.  
 *
 *
 *
 * Keep SDA high while strobing SCL low-high-low....
 *
 * HIGH 
 *            <data>          SDA (data)
 *  LOW  _____      _____
 *
 *
 * HIGH  _____           
 *            <data>          SCL (clock)
 *  LOW             _____ 
 *
 *
 */
unsigned char ct82_i2c_read(void)
{
  unsigned char i, j;
  
  i = 0;
  for (j = 0; j < 8; j++)
    {
      ct82_i2c_regwrite(1, 0);
      i = (i << 1) | ct82_i2c_regread(2);
      ct82_i2c_regwrite(0, 0);
    }

  return i;
}

/*
 * ct82_i2c_busscan()
 * 
 * Scans the buss.  Returns a count of the devices on the line.
 *
 * Basically just bangs a 0 at every possible MAD and sees if it ACKs.
 * 
 */
int ct82_i2c_busscan(void)
{
  int i,j;
  unsigned char ack;

  j = 0;
  for (i = 0; i < 256; i += 2)
    {
      ct82_i2c_start();
      ack = ct82_i2c_sendbyte(i, 0);
      ct82_i2c_stop();
      if (!ack)
	{
	  printf("found device at 0x%02x\n", i);
	  j++;
	}
    } 
  return j;
}

/*
 * ct82_init()
 *
 * Eventually will initialize the chip.  For now, its just where all
 * the fun is.
 *
 */
int ct82_init(void)
{
  unsigned char i = 0x00;
  int j = 0;
 
#if 0
  ct82_write(0xff, ct82_read(0xff) | 0x01);
 
  for (j = 0; j < 1000; j++)
    {
      i = ct82_read(0x18);
      if ((i!=0x33)&&(i!=0x23))
	printf("0x18: %02x\n", i);
      usleep(1);
    }
#endif
 
#if 0
  /* 
   * 
   * cycle through the three available inputs... once per sencond 
   *
   * I'm not real sure how this works because the video input multiplexer
   * according to the books I have, is not connected to the i2c bus.
   * 
   * Amazing to me how this works at all.  Derived from Bernhard's code.
   *
   */
  j = 0;
  while(1)
    {
      if ( (j%3)==0 )
	{
	  printf("going to input 0\n");
	  ct82_i2c_write(0x8a, 10, 2);
	}
      else if ( (j%2) == 0 )
	{
	  printf("going to input 1\n");
	  ct82_i2c_write(0x8a, 10, 42);
	}
      else
	{
	  printf("going to input 2\n");
	  ct82_i2c_write(0x8a, 10, 18);
	}
      j++;
      usleep(1000000);
    }
#endif

  /* try a bus scan */
  j = ct82_i2c_busscan();
  printf("busscan: found %d devices\n", j);
  
  /* set the image brightness to zero and lift it up 2 ticks at a time to 64.*/
  j = 0;
  while (j < 64)
    {
      printf("Brightness: %d\n", j);
      ct82_i2c_write(0x88, 0x00, j); /* the MAD for the TDA4680 is 0x88 */
      j+=2;
      usleep(100000);
    }

  return 0;
}

int main(void)
{
  iopl(3); /* get access to the io ports */
  ct82_init(); /* begin */
}

