2011-11-03 22:21:53 +04:00
/*
* Copyright © 2006 - 2007 Intel Corporation
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Authors :
* Eric Anholt < eric @ anholt . net >
*/
2011-11-16 16:13:30 +04:00
# include <linux/export.h>
2011-11-03 22:21:53 +04:00
# include <linux/i2c.h>
# include <linux/i2c-algo-bit.h>
# include "psb_drv.h"
# include "psb_intel_reg.h"
/*
* Intel GPIO access functions
*/
# define I2C_RISEFALL_TIME 20
static int get_clock ( void * data )
{
struct psb_intel_i2c_chan * chan = data ;
struct drm_device * dev = chan - > drm_dev ;
u32 val ;
val = REG_READ ( chan - > reg ) ;
return ( val & GPIO_CLOCK_VAL_IN ) ! = 0 ;
}
static int get_data ( void * data )
{
struct psb_intel_i2c_chan * chan = data ;
struct drm_device * dev = chan - > drm_dev ;
u32 val ;
val = REG_READ ( chan - > reg ) ;
return ( val & GPIO_DATA_VAL_IN ) ! = 0 ;
}
static void set_clock ( void * data , int state_high )
{
struct psb_intel_i2c_chan * chan = data ;
struct drm_device * dev = chan - > drm_dev ;
u32 reserved = 0 , clock_bits ;
/* On most chips, these bits must be preserved in software. */
reserved =
REG_READ ( chan - > reg ) & ( GPIO_DATA_PULLUP_DISABLE |
GPIO_CLOCK_PULLUP_DISABLE ) ;
if ( state_high )
clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK ;
else
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
GPIO_CLOCK_VAL_MASK ;
REG_WRITE ( chan - > reg , reserved | clock_bits ) ;
udelay ( I2C_RISEFALL_TIME ) ; /* wait for the line to change state */
}
static void set_data ( void * data , int state_high )
{
struct psb_intel_i2c_chan * chan = data ;
struct drm_device * dev = chan - > drm_dev ;
u32 reserved = 0 , data_bits ;
/* On most chips, these bits must be preserved in software. */
reserved =
REG_READ ( chan - > reg ) & ( GPIO_DATA_PULLUP_DISABLE |
GPIO_CLOCK_PULLUP_DISABLE ) ;
if ( state_high )
data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK ;
else
data_bits =
GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
GPIO_DATA_VAL_MASK ;
REG_WRITE ( chan - > reg , reserved | data_bits ) ;
udelay ( I2C_RISEFALL_TIME ) ; /* wait for the line to change state */
}
/**
* psb_intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
* @ dev : DRM device
* @ output : driver specific output device
* @ reg : GPIO reg to use
* @ name : name for this bus
*
* Creates and registers a new i2c bus with the Linux i2c layer , for use
* in output probing and control ( e . g . DDC or SDVO control functions ) .
*
* Possible values for @ reg include :
* % GPIOA
* % GPIOB
* % GPIOC
* % GPIOD
* % GPIOE
* % GPIOF
* % GPIOG
* % GPIOH
* see PRM for details on how these different busses are used .
*/
struct psb_intel_i2c_chan * psb_intel_i2c_create ( struct drm_device * dev ,
const u32 reg , const char * name )
{
struct psb_intel_i2c_chan * chan ;
chan = kzalloc ( sizeof ( struct psb_intel_i2c_chan ) , GFP_KERNEL ) ;
if ( ! chan )
goto out_free ;
chan - > drm_dev = dev ;
chan - > reg = reg ;
snprintf ( chan - > adapter . name , I2C_NAME_SIZE , " intel drm %s " , name ) ;
chan - > adapter . owner = THIS_MODULE ;
chan - > adapter . algo_data = & chan - > algo ;
chan - > adapter . dev . parent = & dev - > pdev - > dev ;
chan - > algo . setsda = set_data ;
chan - > algo . setscl = set_clock ;
chan - > algo . getsda = get_data ;
chan - > algo . getscl = get_clock ;
chan - > algo . udelay = 20 ;
chan - > algo . timeout = usecs_to_jiffies ( 2200 ) ;
chan - > algo . data = chan ;
i2c_set_adapdata ( & chan - > adapter , chan ) ;
if ( i2c_bit_add_bus ( & chan - > adapter ) )
goto out_free ;
/* JJJ: raise SCL and SDA? */
set_data ( chan , 1 ) ;
set_clock ( chan , 1 ) ;
udelay ( 20 ) ;
return chan ;
out_free :
kfree ( chan ) ;
return NULL ;
}
/**
* psb_intel_i2c_destroy - unregister and free i2c bus resources
* @ output : channel to free
*
* Unregister the adapter from the i2c layer , then free the structure .
*/
void psb_intel_i2c_destroy ( struct psb_intel_i2c_chan * chan )
{
if ( ! chan )
return ;
i2c_del_adapter ( & chan - > adapter ) ;
kfree ( chan ) ;
}