2006-06-20 22:55:55 +04:00
/**************************************************************************
Copyright 2006 Dave Airlie < airlied @ linux . ie >
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
on 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
THE COPYRIGHT HOLDERS AND / OR THEIR 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 <linux/module.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/pci.h>
# include <linux/fb.h>
# include <linux/i2c.h>
# include <linux/i2c-algo-bit.h>
# include <asm/io.h>
# include "intelfb.h"
# include "intelfbhw.h"
/* bit locations in the registers */
# define SCL_DIR_MASK 0x0001
# define SCL_DIR 0x0002
# define SCL_VAL_MASK 0x0004
# define SCL_VAL_OUT 0x0008
# define SCL_VAL_IN 0x0010
# define SDA_DIR_MASK 0x0100
# define SDA_DIR 0x0200
# define SDA_VAL_MASK 0x0400
# define SDA_VAL_OUT 0x0800
# define SDA_VAL_IN 0x1000
static void intelfb_gpio_setscl ( void * data , int state )
{
struct intelfb_i2c_chan * chan = data ;
struct intelfb_info * dinfo = chan - > dinfo ;
u32 val ;
2007-10-16 12:29:31 +04:00
OUTREG ( chan - > reg , ( state ? SCL_VAL_OUT : 0 ) |
SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK ) ;
2006-06-20 22:55:55 +04:00
val = INREG ( chan - > reg ) ;
}
static void intelfb_gpio_setsda ( void * data , int state )
{
struct intelfb_i2c_chan * chan = data ;
struct intelfb_info * dinfo = chan - > dinfo ;
u32 val ;
2007-10-16 12:29:31 +04:00
OUTREG ( chan - > reg , ( state ? SDA_VAL_OUT : 0 ) |
SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK ) ;
2006-06-20 22:55:55 +04:00
val = INREG ( chan - > reg ) ;
}
static int intelfb_gpio_getscl ( void * data )
{
struct intelfb_i2c_chan * chan = data ;
struct intelfb_info * dinfo = chan - > dinfo ;
u32 val ;
OUTREG ( chan - > reg , SCL_DIR_MASK ) ;
OUTREG ( chan - > reg , 0 ) ;
val = INREG ( chan - > reg ) ;
return ( ( val & SCL_VAL_IN ) ! = 0 ) ;
}
static int intelfb_gpio_getsda ( void * data )
{
struct intelfb_i2c_chan * chan = data ;
struct intelfb_info * dinfo = chan - > dinfo ;
u32 val ;
OUTREG ( chan - > reg , SDA_DIR_MASK ) ;
OUTREG ( chan - > reg , 0 ) ;
val = INREG ( chan - > reg ) ;
return ( ( val & SDA_VAL_IN ) ! = 0 ) ;
}
static int intelfb_setup_i2c_bus ( struct intelfb_info * dinfo ,
2007-10-16 12:29:31 +04:00
struct intelfb_i2c_chan * chan ,
2008-07-15 00:38:28 +04:00
const u32 reg , const char * name ,
int class )
2006-06-20 22:55:55 +04:00
{
int rc ;
2007-10-16 12:29:31 +04:00
chan - > dinfo = dinfo ;
chan - > reg = reg ;
2007-05-02 01:26:28 +04:00
snprintf ( chan - > adapter . name , sizeof ( chan - > adapter . name ) ,
" intelfb %s " , name ) ;
2008-07-15 00:38:28 +04:00
chan - > adapter . class = class ;
2007-10-16 12:29:31 +04:00
chan - > adapter . owner = THIS_MODULE ;
2006-06-20 22:55:55 +04:00
chan - > adapter . algo_data = & chan - > algo ;
chan - > adapter . dev . parent = & chan - > dinfo - > pdev - > dev ;
2007-10-16 12:29:31 +04:00
chan - > algo . setsda = intelfb_gpio_setsda ;
chan - > algo . setscl = intelfb_gpio_setscl ;
chan - > algo . getsda = intelfb_gpio_getsda ;
chan - > algo . getscl = intelfb_gpio_getscl ;
chan - > algo . udelay = 40 ;
chan - > algo . timeout = 20 ;
chan - > algo . data = chan ;
2006-06-20 22:55:55 +04:00
i2c_set_adapdata ( & chan - > adapter , chan ) ;
/* Raise SCL and SDA */
intelfb_gpio_setsda ( chan , 1 ) ;
intelfb_gpio_setscl ( chan , 1 ) ;
udelay ( 20 ) ;
rc = i2c_bit_add_bus ( & chan - > adapter ) ;
if ( rc = = 0 )
DBG_MSG ( " I2C bus %s registered. \n " , name ) ;
else
WRN_MSG ( " Failed to register I2C bus %s. \n " , name ) ;
return rc ;
}
void intelfb_create_i2c_busses ( struct intelfb_info * dinfo )
{
int i = 0 ;
/* everyone has at least a single analog output */
dinfo - > num_outputs = 1 ;
dinfo - > output [ i ] . type = INTELFB_OUTPUT_ANALOG ;
/* setup the DDC bus for analog output */
2007-10-16 12:29:31 +04:00
intelfb_setup_i2c_bus ( dinfo , & dinfo - > output [ i ] . ddc_bus , GPIOA ,
2008-07-15 00:38:28 +04:00
" CRTDDC_A " , I2C_CLASS_DDC ) ;
2006-06-20 22:55:55 +04:00
i + + ;
2007-10-16 12:29:31 +04:00
/* need to add the output busses for each device
- this function is very incomplete
- i915GM has LVDS and TVOUT for example
*/
switch ( dinfo - > chipset ) {
2006-06-20 22:55:55 +04:00
case INTEL_830M :
case INTEL_845G :
2009-04-14 01:40:10 +04:00
case INTEL_854 :
2006-06-20 22:55:55 +04:00
case INTEL_855GM :
case INTEL_865G :
dinfo - > output [ i ] . type = INTELFB_OUTPUT_DVO ;
2007-10-16 12:29:31 +04:00
intelfb_setup_i2c_bus ( dinfo , & dinfo - > output [ i ] . ddc_bus ,
2008-07-15 00:38:28 +04:00
GPIOD , " DVODDC_D " , I2C_CLASS_DDC ) ;
2007-10-16 12:29:31 +04:00
intelfb_setup_i2c_bus ( dinfo , & dinfo - > output [ i ] . i2c_bus ,
2008-07-15 00:38:28 +04:00
GPIOE , " DVOI2C_E " , 0 ) ;
2006-06-20 22:55:55 +04:00
i + + ;
break ;
case INTEL_915G :
case INTEL_915GM :
2007-10-16 12:29:31 +04:00
/* has some LVDS + tv-out */
2006-06-20 22:55:55 +04:00
case INTEL_945G :
case INTEL_945GM :
2008-10-16 09:03:35 +04:00
case INTEL_945GME :
2008-04-28 13:15:43 +04:00
case INTEL_965G :
case INTEL_965GM :
2006-06-20 22:55:55 +04:00
/* SDVO ports have a single control bus - 2 devices */
dinfo - > output [ i ] . type = INTELFB_OUTPUT_SDVO ;
2007-10-16 12:29:31 +04:00
intelfb_setup_i2c_bus ( dinfo , & dinfo - > output [ i ] . i2c_bus ,
2008-07-15 00:38:28 +04:00
GPIOE , " SDVOCTRL_E " , 0 ) ;
2006-06-20 22:55:55 +04:00
/* TODO: initialize the SDVO */
2007-10-16 12:29:31 +04:00
/* I830SDVOInit(pScrn, i, DVOB); */
2006-06-20 22:55:55 +04:00
i + + ;
/* set up SDVOC */
dinfo - > output [ i ] . type = INTELFB_OUTPUT_SDVO ;
dinfo - > output [ i ] . i2c_bus = dinfo - > output [ i - 1 ] . i2c_bus ;
/* TODO: initialize the SDVO */
2007-10-16 12:29:31 +04:00
/* I830SDVOInit(pScrn, i, DVOC); */
2006-06-20 22:55:55 +04:00
i + + ;
break ;
}
dinfo - > num_outputs = i ;
}
2006-06-20 22:55:55 +04:00
void intelfb_delete_i2c_busses ( struct intelfb_info * dinfo )
{
int i ;
for ( i = 0 ; i < MAX_OUTPUTS ; i + + ) {
if ( dinfo - > output [ i ] . i2c_bus . dinfo ) {
2006-12-10 23:21:33 +03:00
i2c_del_adapter ( & dinfo - > output [ i ] . i2c_bus . adapter ) ;
2006-06-20 22:55:55 +04:00
dinfo - > output [ i ] . i2c_bus . dinfo = NULL ;
}
if ( dinfo - > output [ i ] . ddc_bus . dinfo ) {
2006-12-10 23:21:33 +03:00
i2c_del_adapter ( & dinfo - > output [ i ] . ddc_bus . adapter ) ;
2006-06-20 22:55:55 +04:00
dinfo - > output [ i ] . ddc_bus . dinfo = NULL ;
}
}
}