
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <stdarg.h>
#include <time.h>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <fcntl.h>
#include "i2c-dev.h"
#include <unistd.h>
#include <string.h>

// various register macros
// read register
#define READ_REGISTER_AUXYCOUT_SYNC(X)    ((X & 0x08) == 0x08)
#define READ_REGISTER_TVYCOUT_SYNC(X)     ((X & 0x04) == 0x04)
#define READ_REGISTER_FUNCTIONCONTROLINPUT(X)   (X & 0x03)

// write register 0
#define WRITE_REGISTER_VOLUME(X)    (X&0x3F)
#define WRITE_REGISTER_TV_AUDIO_MUTE(X)  ((X&0x01) << 6)
#define WRITE_REGISTER_AUX_AUDIO_MUTE(X) ((X&0x01) << 7)
// write register 1
#define WRITE_REGISTER_TVAV_SRC(X)     (X&0x07)
#define WRITE_REGISTER_AUXAV_SRC(X) ((X&0x07) << 3)
#define WRITE_REGISTER_TVRGB_MUTE(X)   ((X&0x01) << 6)
#define WRITE_REGISTER_RGB_SYNC_SRC(X) ((X&0x01) << 7)
// write register 2
#define WRITE_REGISTER_CONTROLVOLTAGE(X) (X&0x03)
#define WRITE_REGISTER_CONTROLPIN(X)   ((X&0x03) << 2)
#define WRITE_REGISTER_RGBGAIN(X)      ((X&0x03) << 4)
#define WRITE_REGISTER_BLANKOUTPUT(X)  ((X&0x03) << 6)
// write register 3
#define WRITE_REGISTER_DO0(X)       (X&0x01)
#define WRITE_REGISTER_DO1(X)       ((X&0x01) << 1)
#define WRITE_REGISTER_AUDIOGAIN(X)    ((X&0x03) << 2)
#define WRITE_REGISTER_TV_STEREOCTRL(X)      ((X&0x03) << 4)
#define WRITE_REGISTER_AUX_STEREOCTRL(X)     ((X&0x03) << 6)
// write register 4
#define WRITE_REGISTER_TVVOL_SELECT1(X)   (X&0x01)
#define WRITE_REGISTER_TVVOL_SELECT2(X)   ((X&0x01) << 1)
#define WRITE_REGISTER_GAIN6DB(X)   ((X&0x01) << 2)
#define WRITE_REGISTER_LINEOUT_SRC(X)  ((X&0x01) << 3)

static __u8 predefvals[3][20] = {
 {0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // console/tv-out
 {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // loopthru (Scart-TV)
 {0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00}  // em8400
};

int openi2cadapter(int ano)
{
  int fd;
  char filename[20];

  sprintf(filename,"/dev/i2c-%d",ano);
  if ((fd = open(filename,O_RDWR)) < 0) {
    // do error handling
    fprintf (stderr,"Could not open i2c-adapter: %s\n",filename);
    exit(1);
  }
  return fd;
}

__u8 getdevstate(int fd, int address)
{
  __u8 devstate;

  if (ioctl(fd,I2C_SLAVE,address) < 0) {
    // do error handling
    fprintf (stderr,"No i2c-device with address %X present\n",address);
    close(fd);
    exit(1);
  }

  devstate = i2c_smbus_read_byte(fd);

  if ((devstate & 0x10) != 0x10)
  {
    // no tdk5002c!
    fprintf (stderr,"Device %X is NOT a TDK5002c\n",address);
    close(fd);
    exit(1);
  }

  return devstate;
}

void outputSettings(__u8 devstate)
{
  fprintf (stdout, "TDK5002c read register settings:\n");
  fprintf (stdout,"AUX ycout: %s\n",READ_REGISTER_AUXYCOUT_SYNC(devstate)?"sync pulse present":"no sync pulse");
  fprintf (stdout,"TV ycout: %s\n",READ_REGISTER_TVYCOUT_SYNC(devstate)?"sync pulse present":"no sync pulse");
  
  if (READ_REGISTER_FUNCTIONCONTROLINPUT(devstate) == 0x00)
    fprintf (stdout,"TV_Fnc or AUX_Fnc pin level =Level 0 .~0V\n");
  else if (READ_REGISTER_FUNCTIONCONTROLINPUT(devstate) == 0x01)
    fprintf (stdout,"TV_Fnc or AUX_Fnc pin level =Level 1A ~6.0V\n");
  else if (READ_REGISTER_FUNCTIONCONTROLINPUT(devstate) == 0x02)
    fprintf (stdout,"TV_Fnc or AUX_Fnc pin level = Level 1B ~12V\n");
  else
    fprintf (stdout,"TV_Fnc or AUX_Fnc pin level = _UNKNOWN_ Level - Whats this?\n");
}

void buildRegisters(__u8* regs, __u8* vals)
{
  regs[0] = 0x00 |
   WRITE_REGISTER_VOLUME(vals[0]) |
   WRITE_REGISTER_TV_AUDIO_MUTE(vals[1]) |
   WRITE_REGISTER_AUX_AUDIO_MUTE(vals[2]);
  regs[1] = 0x00 |
   WRITE_REGISTER_TVAV_SRC(vals[3]) |
   WRITE_REGISTER_AUXAV_SRC(vals[4]) |
   WRITE_REGISTER_TVRGB_MUTE(vals[5]) |
   WRITE_REGISTER_RGB_SYNC_SRC(vals[6]);
  regs[2] = 0x00 |
   WRITE_REGISTER_CONTROLVOLTAGE(vals[7]) |
   WRITE_REGISTER_CONTROLPIN(vals[8]) |
   WRITE_REGISTER_RGBGAIN(vals[9]) |
   WRITE_REGISTER_BLANKOUTPUT(vals[10]);
  regs[3] = 0x00 |
   WRITE_REGISTER_DO0(vals[11]) |
   WRITE_REGISTER_DO1(vals[12]) |
   WRITE_REGISTER_AUDIOGAIN(vals[13]) |
   WRITE_REGISTER_TV_STEREOCTRL(vals[14]) |
   WRITE_REGISTER_AUX_STEREOCTRL(vals[15]);
  regs[4] = 0x00 |
   WRITE_REGISTER_TVVOL_SELECT1(vals[16]) |
   WRITE_REGISTER_TVVOL_SELECT2(vals[17]) |
   WRITE_REGISTER_GAIN6DB(vals[18]) |
   WRITE_REGISTER_LINEOUT_SRC(vals[19]);
}

int writeRegisters(int fd, __u8* regs)
{
  __u16 writereg2;

  // shuffle write-register for our superduper workaround ....
  writereg2 = 0x0000 | regs[1] | (regs[2] << 8);
  
  if (i2c_smbus_write_block_data(fd, regs[0], 3, &regs[2]) != 0)
    return -1;
  if (i2c_smbus_write_word_data(fd,regs[0],writereg2) != 0)
    return -1;

  return 0;
}

int readRegisters(int fd, __u8* regs)
{
  __u16 writereg2;
  int readReg;

  // shuffle write-register for our superduper workaround ....
  writereg2 = 0x0000 | regs[1] | (regs[2] << 8);
  
  if (i2c_smbus_read_i2c_block_data(fd, regs[0], &regs[2]) != 3)
    return -1;

  readReg = i2c_smbus_read_word_data(fd,regs[0]);
  if (readReg == -1)
    return -1;

  return 0;
}

int main(int argc, char *argv[])
{
  int file;
  int adapter_nr = 0;   /* i2c-adpater - see /dev/i2c-X ... */
  int addr = 0x48;   /* The tdk5002c i2c address */
  
  __u8 devstate;
  __u8 writereg[5];
  __u8 parms[20];
  int c;
  int sequenceno = -1;
  int onlyinfo = 0;

  while ((c = getopt (argc, argv, "a:i:s:d")) != -1)
         switch (c)
           {
           case 'i':
             if (sscanf(optarg,"%d",&adapter_nr) != 1)
         {
         fprintf (stderr, "Invalid argument for -i\n");
         exit(1);
         }
             break;

           case 'a':
      if (sscanf(optarg,"%X",&addr) != 1)
         {
         fprintf (stderr, "Invalid argument for -a\n");
         exit(1);
         }
             break;

           case 's':
      if ((sscanf(optarg,"%d",&sequenceno) != 1) || (sequenceno < 0) || (sequenceno > 2))
         {
         fprintf (stderr, "Invalid argument for -s\n");
         exit(1);
         }
             break;
   
       case 'd':
      onlyinfo = 1;
      break;

           case '?':
             if (isprint (optopt))
               fprintf (stderr, "Unknown option `-%c'.\n", optopt);
             else
               fprintf (stderr,
                        "Unknown option character `\\x%x'.\n",
                        optopt);
             return 1;
           default:
             exit(1);
  }

  if ((sequenceno > -1) && (!onlyinfo))
  {
   fprintf(stdout,"Will use sequence %d\n",sequenceno);
   memcpy(parms,predefvals[sequenceno],20);
  }
  else if (!onlyinfo)
  {
   int index;
   int no = 0;
   fprintf(stdout,"Will use given parameters\n");

       for (index = optind; index < argc; index++)
   {
     if (sscanf(argv[index],"%X",&parms[index-optind]) != 1)
        {   
         fprintf(stderr,"Parameter %d is not in hex-format\n",(index-optind+1));
         exit(1);
        }

     no++;
   }

   if (no < 20)
   {
     fprintf(stdout,"Not enough parameters given - will exit\n");
     exit(0);
   }

   if (no > 20)
   {
     fprintf(stdout,"Too much parameters given - will exit\n");
     exit(0);
   }
  }

  // open device,get info and print info
  file = openi2cadapter(adapter_nr);
  devstate = getdevstate(file, addr);
  outputSettings(devstate);

  if (onlyinfo)
   exit(0);

  // build write register content
  buildRegisters(writereg, parms);

  fprintf(stdout,"About to write (0-4): %X %X %X %X %X\n",writereg[0],writereg[1],writereg[2],writereg[3],writereg[4]);
   
  if (writeRegisters(file,writereg) != 0)
    fprintf(stderr,"Error while writing data to tdk\n");
  else
    fprintf(stdout,"Succ. written data to tdk\n");

  close(file);
  
  exit(0);
}

