#!   /usr/bin/env   python
#    coding: utf8

# Copyright CERN, 2011
# Author: Matthieu Cattin (CERN)
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Last modifications: 27/4/2012

# Import standard modules
import sys
import time

# Import specific modules
from spi import *

# Class to access the LTC217x (ADC) chip.
# It uses the SPI class.

class LTC217xOperationError(Exception):
    def __init__(self, addr, msg):
        self.msg = msg
        self.addr = addr # addr corresponds to the SPI slave number
    def __str__(self):
        return ("LTC217x [SPI slave number:%d]: %s" %(self.addr, self.msg))

class CLTC217x:

    # LTC217x registers
    R_RST = 0x00
    R_FMT = 0x01
    R_OUTMODE = 0x02
    R_TESTPAT_MSB = 0x03
    R_TESTPAT_LSB = 0x04

    RST = (1<<7)

    FMT_DCSOFF = (1<<7)
    FMT_RAND = (1<<6)
    FMT_TWOSCOMP = (1<<5)
    FMT_SLEEP = (1<<4)
    FMT_CH4_NAP = (1<<3)
    FMT_CH3_NAP = (1<<2)
    FMT_CH2_NAP = (1<<1)
    FMT_CH1_NAP = (1<<0)

    OUTMODE_ILVDS_MASK = 0xE0
    OUTMODE_ILVDS_SHIFT = 5
    OUTMODE_TERMON = (1<<4)
    OUTMODE_OUTOFF = (1<<3)
    OUTMODE_MASK = 0x07

    TESTPAT_MSB_OUTTEST = (1<<7)
    TESTPAT_MSB_MASK = 0x3F
    TESTPAT_LSB_MASK = 0xFF

    # Constant definitions
    ILVDS = {'3.5mA':0x0, '4.0mA':0x1, '4.5mA':0x2, '3.0mA':0x4, '2.5mA':0x5, '2.1mA':0x6, '1.75mA':0x7}
    OUTMODE = {'2lanes16bit':0x0, '2lanes14bit':0x1, '2lanes12bit':0x2, '1lane14bit':0x5, '1lane12bit':0x6, '1lane16bit':0x7}

    # addr = ltc217x register address (1 byte)
    # value = value to write to the register (1 byte)
    def wr_reg(self, addr, value):
        try:
            tx = [value, addr]
            self.spi.transaction(self.slave, tx)
        except SPIDeviceOperationError as e:
            raise LTC217xOperationError(self.slave, e)

    # addr = ltc217x register address (1 byte)
    # return = value of the register (1 byte)
    def rd_reg(self, addr):
        try:
            tx = [0xFF, (addr | 0x80)]
            rx = self.spi.transaction(self.slave, tx)
            return (rx[0] & 0xFF)
        except SPIDeviceOperationError as e:
            raise LTC217xOperationError(self.slave, e)

    def __init__(self, spi, slave):
        self.spi = spi
        self.slave = slave
        self.wr_reg(self.R_RST, self.RST)
        self.wr_reg(self.R_FMT, self.FMT_TWOSCOMP)
        self.wr_reg(self.R_OUTMODE, ((self.ILVDS['4.5mA']  << self.OUTMODE_ILVDS_SHIFT) | (self.OUTMODE['2lanes16bit'])))
        #self.wr_reg(self.R_FMT, 0)
        #self.wr_reg(self.R_OUTMODE, (self.OUTMODE_ILVDS_4M5 | self.OUTMODE_2L_16B | self.OUTMODE_TERMON))
        #self.wr_reg(self.R_OUTMODE, (self.OUTMODE_ILVDS_2M5 | self.OUTMODE_2L_16B | self.OUTMODE_TERMON))

    def reset(self):
        self.wr_reg(self.R_RST, self.RST)

    def get_fmt(self):
        return self.rd_reg(self.R_FMT)

    def set_fmt(self, value):
        self.wr_reg(self.R_FMT, value)
        return self.rd_reg(self.R_FMT)

    def channel_nap(self, ch, en):
        reg = self.rd_reg(self.R_FMT)
        if(en):
            reg |= (1 << (ch-1))
        else:
            reg &= ~(1 << (ch-1))
        self.wr_reg(self.R_FMT, reg)

    def sleep(self, en):
        reg = self.rd_reg(self.R_FMT)
        if(en):
            reg |= self.FMT_SLEEP
        else:
            reg &= ~(self.FMT_SLEEP)
        self.wr_reg(self.R_FMT, reg)

    def twos_complement(self, en):
        reg = self.rd_reg(self.R_FMT)
        if(en):
            # enable tow's complement data output format
            reg |= self.FMT_TWOSCOMP
        else:
            # binary offset data output format
            reg &= ~(self.FMT_TWOSCOMP)
        self.wr_reg(self.R_FMT, reg)

    def random_out(self, en):
        reg = self.rd_reg(self.R_FMT)
        if(en):
            # Enable random data output
            reg |= self.FMT_RAND
        else:
            # Disable random data output
            reg &= ~(self.FMT_RAND)
        self.wr_reg(self.R_FMT, reg)

    def duty_cycle_stabiliser_dis(self, dis):
        reg = self.rd_reg(self.R_FMT)
        if(dis):
            reg |= self.FMT_DCSOFF
        else:
            reg &= ~(self.FMT_DCSOFF)
        self.wr_reg(self.R_FMT, reg)

    def get_outmode(self):
        return self.rd_reg(self.R_OUTMODE)

    def set_outmode(self, value):
        self.wr_reg(self.R_OUTMODE, value)
        return self.rd_reg(self.R_OUTMODE)

    def set_i_lvds(self, current):
        if current in self.ILVDS:
            #print "[LTC217x] LVDS settting: %s -> 0x%02X" %(current, self.ILVDS[current])
            reg = self.rd_reg(self.R_OUTMODE) # Read register
            #print "[LTC217x] Output mode reg: 0x%02X" %(reg)
            reg &= ~(self.OUTMODE_ILVDS_MASK) # Reset ILVDS bits
            #print "[LTC217x] Output mode reg: 0x%02X" %(reg)
            reg |= (self.OUTMODE_ILVDS_MASK & (self.ILVDS[current] << self.OUTMODE_ILVDS_SHIFT))
            #print "[LTC217x] Output mode reg: 0x%02X" %(reg)
            self.wr_reg(self.R_OUTMODE, reg) # Write new value to register
        else:
            raise LTC217xOperationError(self.slave, "Requiered LVDS current setting (%s) doesn't exist for LPC217x!" % current)

    def set_outmode(self, mode):
        if mode in self.OUTMODE:
            #print "[LTC217x] Output mode settting: %s" %(mode)
            reg = self.rd_reg(self.R_OUTMODE) # Read register
            #print "[LTC217x] Ouput mode reg: 0x%02X" %(reg)
            reg &= ~(self.OUTMODE_MASK) # Reset ILVDS bits
            #print "[LTC217x] Ouput mode reg: 0x%02X" %(reg)
            reg |= self.OUTMODE_MASK & (self.OUTMODE[mode])
            #print "[LTC217x] Output mode reg: 0x%02X" %(reg)
            self.wr_reg(self.R_OUTMODE, reg) # Write new value to register
        else:
            raise LTC217xOperationError(self.slave, "Requiered output mode setting (%s) doesn't exist for LPC217x!" % mode)

    def set_lvds_termination(self, en):
        reg = self.rd_reg(self.R_OUTMODE)
        if(en):
            # Enable LVDS serial terminations
            reg |= self.OUTMODE_TERMON
        else:
            # Disable LVDS serial terminations
            reg &= ~(self.OUTMODE_TERMON)
        self.wr_reg(self.R_OUTMODE, reg)

    def output_dis(self, dis):
        reg = self.rd_reg(self.R_OUTMODE)
        if(dis):
            # Disable LVDS outputs
            reg |= self.OUTMODE_OUTOFF
        else:
            # Enable LVDS outputs
            reg &= ~(self.OUTMODE_OUTOFF)
        self.wr_reg(self.R_OUTMODE, reg)

    def get_testpat(self):
        return (((self.rd_reg(self.R_TESTPAT_MSB) & self.TESTPAT_MSB_MASK)<<8)
                + (self.rd_reg(self.R_TESTPAT_LSB) & self.TESTPAT_LSB_MASK))

    def get_testpat_stat(self):
        return ((self.rd_reg(self.R_TESTPAT_MSB))>>7)

    def set_testpat(self, pattern):
        self.wr_reg(self.R_TESTPAT_MSB, ((pattern>>8) & self.TESTPAT_MSB_MASK))
        self.wr_reg(self.R_TESTPAT_LSB, (pattern & self.TESTPAT_LSB_MASK))

    def en_testpat(self):
        reg = self.rd_reg(self.R_TESTPAT_MSB)
        reg |= self.TESTPAT_MSB_OUTTEST
        self.wr_reg(self.R_TESTPAT_MSB, reg)

    def dis_testpat(self):
        reg = self.rd_reg(self.R_TESTPAT_MSB)
        reg &= ~self.TESTPAT_MSB_OUTTEST
        self.wr_reg(self.R_TESTPAT_MSB, reg)

    def print_regs(self):
        print '\nLTC217x registers:'
        for i in range(0,5):
            print("reg %d: %.2X") % (i, self.rd_reg(i))
