2005-04-16 15:20:36 -07:00
/*
* linux / drivers / video / nvidia / nvidia - i2c . c - nVidia i2c
*
* Copyright 2004 Antonino A . Daplas < adaplas @ pol . net >
*
* Based on rivafb - i2c . c
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive
* for more details .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/delay.h>
# include <linux/pci.h>
# include <linux/fb.h>
# include <asm/io.h>
# include "nv_type.h"
# include "nv_local.h"
# include "nv_proto.h"
# include "../edid.h"
static void nvidia_gpio_setscl ( void * data , int state )
{
struct nvidia_i2c_chan * chan = data ;
struct nvidia_par * par = chan - > par ;
u32 val ;
VGA_WR08 ( par - > PCIO , 0x3d4 , chan - > ddc_base + 1 ) ;
val = VGA_RD08 ( par - > PCIO , 0x3d5 ) & 0xf0 ;
if ( state )
val | = 0x20 ;
else
val & = ~ 0x20 ;
VGA_WR08 ( par - > PCIO , 0x3d4 , chan - > ddc_base + 1 ) ;
VGA_WR08 ( par - > PCIO , 0x3d5 , val | 0x1 ) ;
}
static void nvidia_gpio_setsda ( void * data , int state )
{
2006-01-09 20:53:02 -08:00
struct nvidia_i2c_chan * chan = data ;
2005-04-16 15:20:36 -07:00
struct nvidia_par * par = chan - > par ;
u32 val ;
VGA_WR08 ( par - > PCIO , 0x3d4 , chan - > ddc_base + 1 ) ;
val = VGA_RD08 ( par - > PCIO , 0x3d5 ) & 0xf0 ;
if ( state )
val | = 0x10 ;
else
val & = ~ 0x10 ;
VGA_WR08 ( par - > PCIO , 0x3d4 , chan - > ddc_base + 1 ) ;
VGA_WR08 ( par - > PCIO , 0x3d5 , val | 0x1 ) ;
}
static int nvidia_gpio_getscl ( void * data )
{
2006-01-09 20:53:02 -08:00
struct nvidia_i2c_chan * chan = data ;
2005-04-16 15:20:36 -07:00
struct nvidia_par * par = chan - > par ;
u32 val = 0 ;
VGA_WR08 ( par - > PCIO , 0x3d4 , chan - > ddc_base ) ;
if ( VGA_RD08 ( par - > PCIO , 0x3d5 ) & 0x04 )
val = 1 ;
val = VGA_RD08 ( par - > PCIO , 0x3d5 ) ;
return val ;
}
static int nvidia_gpio_getsda ( void * data )
{
2006-01-09 20:53:02 -08:00
struct nvidia_i2c_chan * chan = data ;
2005-04-16 15:20:36 -07:00
struct nvidia_par * par = chan - > par ;
u32 val = 0 ;
VGA_WR08 ( par - > PCIO , 0x3d4 , chan - > ddc_base ) ;
if ( VGA_RD08 ( par - > PCIO , 0x3d5 ) & 0x08 )
val = 1 ;
return val ;
}
static int nvidia_setup_i2c_bus ( struct nvidia_i2c_chan * chan , const char * name )
{
int rc ;
strcpy ( chan - > adapter . name , name ) ;
chan - > adapter . owner = THIS_MODULE ;
2005-08-11 23:51:10 +02:00
chan - > adapter . id = I2C_HW_B_NVIDIA ;
2005-04-16 15:20:36 -07:00
chan - > adapter . algo_data = & chan - > algo ;
chan - > adapter . dev . parent = & chan - > par - > pci_dev - > dev ;
chan - > algo . setsda = nvidia_gpio_setsda ;
chan - > algo . setscl = nvidia_gpio_setscl ;
chan - > algo . getsda = nvidia_gpio_getsda ;
chan - > algo . getscl = nvidia_gpio_getscl ;
chan - > algo . udelay = 40 ;
chan - > algo . timeout = msecs_to_jiffies ( 2 ) ;
chan - > algo . data = chan ;
i2c_set_adapdata ( & chan - > adapter , chan ) ;
/* Raise SCL and SDA */
nvidia_gpio_setsda ( chan , 1 ) ;
nvidia_gpio_setscl ( chan , 1 ) ;
udelay ( 20 ) ;
rc = i2c_bit_add_bus ( & chan - > adapter ) ;
if ( rc = = 0 )
dev_dbg ( & chan - > par - > pci_dev - > dev ,
" I2C bus %s registered. \n " , name ) ;
else {
dev_warn ( & chan - > par - > pci_dev - > dev ,
" Failed to register I2C bus %s. \n " , name ) ;
chan - > par = NULL ;
}
return rc ;
}
void nvidia_create_i2c_busses ( struct nvidia_par * par )
{
par - > bus = 3 ;
par - > chan [ 0 ] . par = par ;
par - > chan [ 1 ] . par = par ;
par - > chan [ 2 ] . par = par ;
par - > chan [ 0 ] . ddc_base = 0x3e ;
2006-01-09 20:52:57 -08:00
nvidia_setup_i2c_bus ( & par - > chan [ 0 ] , " nvidia #0 " ) ;
2005-04-16 15:20:36 -07:00
par - > chan [ 1 ] . ddc_base = 0x36 ;
2006-01-09 20:52:57 -08:00
nvidia_setup_i2c_bus ( & par - > chan [ 1 ] , " nvidia #1 " ) ;
2005-04-16 15:20:36 -07:00
par - > chan [ 2 ] . ddc_base = 0x50 ;
2006-01-09 20:52:57 -08:00
nvidia_setup_i2c_bus ( & par - > chan [ 2 ] , " nvidia #2 " ) ;
2005-04-16 15:20:36 -07:00
}
void nvidia_delete_i2c_busses ( struct nvidia_par * par )
{
if ( par - > chan [ 0 ] . par )
i2c_bit_del_bus ( & par - > chan [ 0 ] . adapter ) ;
par - > chan [ 0 ] . par = NULL ;
if ( par - > chan [ 1 ] . par )
i2c_bit_del_bus ( & par - > chan [ 1 ] . adapter ) ;
par - > chan [ 1 ] . par = NULL ;
if ( par - > chan [ 2 ] . par )
i2c_bit_del_bus ( & par - > chan [ 2 ] . adapter ) ;
par - > chan [ 2 ] . par = NULL ;
}
static u8 * nvidia_do_probe_i2c_edid ( struct nvidia_i2c_chan * chan )
{
u8 start = 0x0 ;
struct i2c_msg msgs [ ] = {
{
. addr = 0x50 ,
. len = 1 ,
. buf = & start ,
} , {
. addr = 0x50 ,
. flags = I2C_M_RD ,
. len = EDID_LENGTH ,
} ,
} ;
u8 * buf ;
if ( ! chan - > par )
return NULL ;
buf = kmalloc ( EDID_LENGTH , GFP_KERNEL ) ;
if ( ! buf ) {
dev_warn ( & chan - > par - > pci_dev - > dev , " Out of memory! \n " ) ;
return NULL ;
}
msgs [ 1 ] . buf = buf ;
if ( i2c_transfer ( & chan - > adapter , msgs , 2 ) = = 2 )
return buf ;
dev_dbg ( & chan - > par - > pci_dev - > dev , " Unable to read EDID block. \n " ) ;
kfree ( buf ) ;
return NULL ;
}
2005-09-09 13:04:36 -07:00
int nvidia_probe_i2c_connector ( struct fb_info * info , int conn , u8 * * out_edid )
2005-04-16 15:20:36 -07:00
{
2005-09-09 13:04:36 -07:00
struct nvidia_par * par = info - > par ;
2005-04-16 15:20:36 -07:00
u8 * edid = NULL ;
int i ;
for ( i = 0 ; i < 3 ; i + + ) {
/* Do the real work */
edid = nvidia_do_probe_i2c_edid ( & par - > chan [ conn - 1 ] ) ;
if ( edid )
break ;
}
2005-09-09 13:04:36 -07:00
if ( ! edid & & conn = = 1 ) {
/* try to get from firmware */
2005-09-14 14:19:15 -07:00
const u8 * e = fb_firmware_edid ( info - > device ) ;
if ( e ! = NULL ) {
edid = kmalloc ( EDID_LENGTH , GFP_KERNEL ) ;
if ( edid )
memcpy ( edid , e , EDID_LENGTH ) ;
}
2005-09-09 13:04:36 -07:00
}
2006-03-27 01:17:33 -08:00
* out_edid = edid ;
2005-04-16 15:20:36 -07:00
2005-09-09 13:04:36 -07:00
return ( edid ) ? 0 : 1 ;
2005-04-16 15:20:36 -07:00
}