/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/via/via_i2c.c,v 1.3 2003/08/27 15:16:09 tsi Exp $ */ /* * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sub license, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "xf86.h" #include "xf86_OSproc.h" #include "xf86_ansic.h" #include "compiler.h" #include "xf86Pci.h" #include "xf86PciInfo.h" #include "vgaHW.h" #include "via_driver.h" /* * DDC2 support requires DDC_SDA_MASK and DDC_SCL_MASK */ #define DDC_SDA_READ_MASK (1 << 2) #define DDC_SCL_READ_MASK (1 << 3) #define DDC_SDA_WRITE_MASK (1 << 4) #define DDC_SCL_WRITE_MASK (1 << 5) /* I2C Function for DDC2 */ static void VIAI2C1PutBits(I2CBusPtr b, int clock, int data) { CARD8 reg; outb(0x3c4, 0x26); reg = inb(0x3c5); reg &= 0xF0; reg |= 0x01; /* Enable DDC */ if (clock) reg |= DDC_SCL_WRITE_MASK; else reg &= ~DDC_SCL_WRITE_MASK; if (data) reg |= DDC_SDA_WRITE_MASK; else reg &= ~DDC_SDA_WRITE_MASK; outb(0x3c4, 0x26); outb(0x3c5, reg); } static void VIAI2C1GetBits(I2CBusPtr b, int *clock, int *data) { CARD8 reg; outb(0x3c4, 0x26); reg = inb(0x3c5); *clock = (reg & DDC_SCL_READ_MASK) != 0; *data = (reg & DDC_SDA_READ_MASK) != 0; } /* Function for DVI DDC2. Also used for the tuner and TV IC's */ static void VIAI2C2PutBits(I2CBusPtr b, int clock, int data) { CARD8 reg; outb(0x3c4, 0x31); reg = inb(0x3c5); reg &= 0xF0; reg |= 0x01; /* Enable DDC */ if (clock) reg |= DDC_SCL_WRITE_MASK; else reg &= ~DDC_SCL_WRITE_MASK; if (data) reg |= DDC_SDA_WRITE_MASK; else reg &= ~DDC_SDA_WRITE_MASK; outb(0x3c4, 0x31); outb(0x3c5, reg); } static void VIAI2C2GetBits(I2CBusPtr b, int *clock, int *data) { CARD8 reg; outb(0x3c4, 0x31); reg = inb(0x3c5); *clock = (reg & DDC_SCL_READ_MASK) != 0; *data = (reg & DDC_SDA_READ_MASK) != 0; } Bool VIAI2CInit(ScrnInfoPtr pScrn) { VIAPtr pVia = VIAPTR(pScrn); I2CBusPtr I2CPtr1, I2CPtr2; I2CPtr1 = xf86CreateI2CBusRec(); I2CPtr2 = xf86CreateI2CBusRec(); if (!I2CPtr1 || !I2CPtr2) return FALSE; I2CPtr1->BusName = "I2C bus 1"; I2CPtr1->scrnIndex = pScrn->scrnIndex; I2CPtr1->I2CPutBits = VIAI2C1PutBits; I2CPtr1->I2CGetBits = VIAI2C1GetBits; I2CPtr2->BusName = "I2C bus 2"; I2CPtr2->scrnIndex = pScrn->scrnIndex; I2CPtr2->I2CPutBits = VIAI2C2PutBits; I2CPtr2->I2CGetBits = VIAI2C2GetBits; if (!xf86I2CBusInit(I2CPtr1) || !xf86I2CBusInit(I2CPtr2)) return FALSE; pVia->I2C_Port1 = I2CPtr1; pVia->I2C_Port2 = I2CPtr2; return TRUE; } #ifdef _MY_I2C_ /*------------------------------------------------ I2C Module ------------------------------------------------*/ static int pcI2CIndex = 0x3c4; static int pcI2Cport = 0x3c5; /* static int pcIndexReg = 0x31; static int pcSDAReadBack= 0x31; */ static int gSDA=0; #if 0 static void I2CUDelay(int usec) { long b_secs, b_usecs; long a_secs, a_usecs; long d_secs, d_usecs; long diff; if (usec > 0) { xf86getsecs(&b_secs, &b_usecs); do { /* It would be nice to use {xf86}usleep, * but usleep (1) takes >10000 usec ! */ xf86getsecs(&a_secs, &a_usecs); d_secs = (a_secs - b_secs); d_usecs = (a_usecs - b_usecs); diff = d_secs*1000000 + d_usecs; } while (diff>0 && diff< (usec + 1)); } } #endif /* Enable I2C */ void I2C_Enable(int pcIndexReg) { int tempI2Cdata, Reg3C4H; /* save 3c4H */ Reg3C4H = inb(pcI2CIndex); outb(pcI2CIndex, pcIndexReg); tempI2Cdata = inb(pcI2Cport); tempI2Cdata |= 0x01; /* Bit 0:I2C Serial Port Enable */ outb(pcI2Cport, tempI2Cdata); /* restore 3c4H */ outb(pcI2CIndex, Reg3C4H); } /* I2C_enable */ /* reverse data */ long I2C_reverk(register unsigned data) { unsigned long rdata = 0; int i; for ( i = 0; i < 16 ; i++ ) { rdata |= ( data & 1 ); /* strip off LSBIT */ data >>= 1; rdata <<= 1; } return(rdata >> 1); } /* I2C_reverk */ /* get an acknowledge back from a slave device */ int I2C_ack_pc(int pcIndexReg) { int ack; I2C_regwrit_pc(pcIndexReg, I2C_SDA, 1); I2C_regwrit_pc(pcIndexReg, I2C_SCL, HICLK); ack = I2C_regread_pc(pcIndexReg, I2C_SDA); I2C_regwrit_pc(pcIndexReg, I2C_SCL, LOCLK); return (ack); } /* I2C_ack_pc */ /* send a start condition */ void I2C_start_pc(int pcIndexReg) { I2C_regwrit_pc(pcIndexReg, I2C_SDA, 1); I2C_regwrit_pc(pcIndexReg, I2C_SCL, HICLK); I2C_regwrit_pc(pcIndexReg, I2C_SDA, 0); I2C_regwrit_pc(pcIndexReg, I2C_SCL, LOCLK); } /* I2C_start_pc */ /* send a stop condition */ void I2C_stop_pc(int pcIndexReg) { I2C_regwrit_pc(pcIndexReg, I2C_SDA, 0); I2C_regwrit_pc(pcIndexReg, I2C_SCL, HICLK); I2C_regwrit_pc(pcIndexReg, I2C_SDA, 1); } /* I2C_stop_pc */ /* write I2C data */ int I2C_wdata_pc(int pcIndexReg, unsigned type , unsigned data) { int i; data = (unsigned int)(I2C_reverk(data) >> 8); /* MSBIT goes out first */ if ( type == I2C_ADR ) I2C_start_pc(pcIndexReg); for ( i = 0; i < 8; data >>=1, i++ ) { I2C_regwrit_pc(pcIndexReg, I2C_SDA, data); I2C_regwrit_pc(pcIndexReg, I2C_SCL, HICLK); I2C_regwrit_pc(pcIndexReg, I2C_SCL, LOCLK); } return I2C_ack_pc(pcIndexReg); /* wait for acknowledge */ } /* I2C_wdata_pc */ /* Write SCL/SDA bit */ void I2C_regwrit_pc(int pcIndexReg, unsigned type, unsigned data ) { int tempI2Cdata, Reg3C4H; /* save 3c4H */ Reg3C4H = inb(pcI2CIndex); outb(pcI2CIndex, pcIndexReg); tempI2Cdata = inb(pcI2Cport); switch (type) { case I2C_SCL: tempI2Cdata &= 0xcf; /* bit5 SPCLCK, bit4 SDATA */ tempI2Cdata |= gSDA | ( (data & 1)<< 5); outb(pcI2Cport, tempI2Cdata); break; case I2C_SDA: tempI2Cdata &= 0xef; tempI2Cdata |= ( (data & 1) << 4); outb(pcI2Cport, tempI2Cdata); gSDA = 0; gSDA = ( (data & 1) << 4); break; } /* restore 3c4H */ outb(pcI2CIndex, Reg3C4H); } /* I2C_regwrit_pc */ /* Read SDA bit */ int I2C_regread_pc(int pcIndexReg, unsigned type) { int temp=0,Reg3C4H; /* save 3c4H */ Reg3C4H = inb(pcI2CIndex); switch (type) { case I2C_SCL : break; case I2C_SDA: outb(pcI2CIndex, pcIndexReg); temp = ( inb(pcI2Cport) >> 2) & 0x01; break; } /* restore 3c4H */ outb(pcI2CIndex, Reg3C4H); return(temp); } /* I2C_regread_pc */ void I2C_wdata(int pcIndexReg, int addr, int subAddr, int data) { int ack = 1; ack = I2C_wdata_pc(pcIndexReg, I2C_ADR, addr); ack = I2C_wdata_pc(pcIndexReg, I2C_DAT, subAddr); ack = I2C_wdata_pc(pcIndexReg, I2C_DAT, data); I2C_stop_pc(pcIndexReg); } int I2C_rdata(int pcIndexReg, int addr, unsigned subAddr) { int StatusData =0, data, i; I2C_wdata_pc(pcIndexReg, I2C_ADR, addr); I2C_wdata_pc(pcIndexReg, I2C_DAT, subAddr); I2C_stop_pc(pcIndexReg); I2C_wdata_pc(pcIndexReg, I2C_ADR, addr+1); /* pull SDA High */ I2C_regwrit_pc(pcIndexReg, I2C_SDA, 1); /* Read Register */ for ( i = 0; i <= 7 ; i++ ) { I2C_regwrit_pc(pcIndexReg, I2C_SCL, HICLK); data = I2C_regread_pc(pcIndexReg, I2C_SDA); I2C_regwrit_pc(pcIndexReg, I2C_SCL, LOCLK); data &= 0x01; /* Keep SDA only */ StatusData <<= 1; StatusData |= data; } I2C_stop_pc(pcIndexReg); return(StatusData); } Bool I2C_Write(int pcIndexReg, int addr, unsigned char *WriteBuffer, int nWrite) { int s = 0; int ack = 1; ack = I2C_wdata_pc(pcIndexReg, I2C_ADR, addr); if (nWrite > 0) { for (; nWrite > 0; WriteBuffer++, nWrite--) ack = I2C_wdata_pc(pcIndexReg, I2C_DAT, *WriteBuffer); s++; } else { I2C_stop_pc(pcIndexReg); return (s); } I2C_stop_pc(pcIndexReg); return (s); } #endif