2011-11-03 18:22:26 +00:00
/*
* Copyright © 2010 Intel Corporation
*
* 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 , sublicense ,
* 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 NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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 .
*
* Authors :
* Li Peng < peng . li @ intel . com >
*/
2011-11-16 12:13:30 +00:00
# include <linux/export.h>
2011-11-03 18:22:26 +00:00
# include <linux/mutex.h>
# include <linux/pci.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include "psb_drv.h"
# define HDMI_READ(reg) readl(hdmi_dev->regs + (reg))
# define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg))
# define HDMI_HCR 0x1000
# define HCR_DETECT_HDP (1 << 6)
# define HCR_ENABLE_HDCP (1 << 5)
# define HCR_ENABLE_AUDIO (1 << 2)
# define HCR_ENABLE_PIXEL (1 << 1)
# define HCR_ENABLE_TMDS (1 << 0)
# define HDMI_HICR 0x1004
# define HDMI_INTR_I2C_ERROR (1 << 4)
# define HDMI_INTR_I2C_FULL (1 << 3)
# define HDMI_INTR_I2C_DONE (1 << 2)
# define HDMI_INTR_HPD (1 << 0)
# define HDMI_HSR 0x1008
# define HDMI_HISR 0x100C
# define HDMI_HI2CRDB0 0x1200
# define HDMI_HI2CHCR 0x1240
# define HI2C_HDCP_WRITE (0 << 2)
# define HI2C_HDCP_RI_READ (1 << 2)
# define HI2C_HDCP_READ (2 << 2)
# define HI2C_EDID_READ (3 << 2)
# define HI2C_READ_CONTINUE (1 << 1)
# define HI2C_ENABLE_TRANSACTION (1 << 0)
# define HDMI_ICRH 0x1100
# define HDMI_HI2CTDR0 0x1244
# define HDMI_HI2CTDR1 0x1248
# define I2C_STAT_INIT 0
# define I2C_READ_DONE 1
# define I2C_TRANSACTION_DONE 2
struct hdmi_i2c_dev {
struct i2c_adapter * adap ;
struct mutex i2c_lock ;
struct completion complete ;
int status ;
struct i2c_msg * msg ;
int buf_offset ;
} ;
static void hdmi_i2c_irq_enable ( struct oaktrail_hdmi_dev * hdmi_dev )
{
u32 temp ;
temp = HDMI_READ ( HDMI_HICR ) ;
temp | = ( HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE ) ;
HDMI_WRITE ( HDMI_HICR , temp ) ;
HDMI_READ ( HDMI_HICR ) ;
}
static void hdmi_i2c_irq_disable ( struct oaktrail_hdmi_dev * hdmi_dev )
{
HDMI_WRITE ( HDMI_HICR , 0x0 ) ;
HDMI_READ ( HDMI_HICR ) ;
}
static int xfer_read ( struct i2c_adapter * adap , struct i2c_msg * pmsg )
{
struct oaktrail_hdmi_dev * hdmi_dev = i2c_get_adapdata ( adap ) ;
struct hdmi_i2c_dev * i2c_dev = hdmi_dev - > i2c_dev ;
u32 temp ;
i2c_dev - > status = I2C_STAT_INIT ;
i2c_dev - > msg = pmsg ;
i2c_dev - > buf_offset = 0 ;
2013-11-14 14:32:02 -08:00
reinit_completion ( & i2c_dev - > complete ) ;
2011-11-03 18:22:26 +00:00
/* Enable I2C transaction */
temp = ( ( pmsg - > len ) < < 20 ) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION ;
HDMI_WRITE ( HDMI_HI2CHCR , temp ) ;
HDMI_READ ( HDMI_HI2CHCR ) ;
while ( i2c_dev - > status ! = I2C_TRANSACTION_DONE )
wait_for_completion_interruptible_timeout ( & i2c_dev - > complete ,
10 * HZ ) ;
return 0 ;
}
static int xfer_write ( struct i2c_adapter * adap , struct i2c_msg * pmsg )
{
/*
* XXX : i2c write seems isn ' t useful for EDID probe , don ' t do anything
*/
return 0 ;
}
static int oaktrail_hdmi_i2c_access ( struct i2c_adapter * adap ,
struct i2c_msg * pmsg ,
int num )
{
struct oaktrail_hdmi_dev * hdmi_dev = i2c_get_adapdata ( adap ) ;
struct hdmi_i2c_dev * i2c_dev = hdmi_dev - > i2c_dev ;
2012-03-08 16:13:23 +00:00
int i ;
2011-11-03 18:22:26 +00:00
mutex_lock ( & i2c_dev - > i2c_lock ) ;
/* Enable i2c unit */
HDMI_WRITE ( HDMI_ICRH , 0x00008760 ) ;
/* Enable irq */
hdmi_i2c_irq_enable ( hdmi_dev ) ;
for ( i = 0 ; i < num ; i + + ) {
if ( pmsg - > len & & pmsg - > buf ) {
if ( pmsg - > flags & I2C_M_RD )
2012-03-08 16:13:23 +00:00
xfer_read ( adap , pmsg ) ;
2011-11-03 18:22:26 +00:00
else
2012-03-08 16:13:23 +00:00
xfer_write ( adap , pmsg ) ;
2011-11-03 18:22:26 +00:00
}
pmsg + + ; /* next message */
}
/* Disable irq */
hdmi_i2c_irq_disable ( hdmi_dev ) ;
mutex_unlock ( & i2c_dev - > i2c_lock ) ;
return i ;
}
static u32 oaktrail_hdmi_i2c_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR ;
}
static const struct i2c_algorithm oaktrail_hdmi_i2c_algorithm = {
. master_xfer = oaktrail_hdmi_i2c_access ,
. functionality = oaktrail_hdmi_i2c_func ,
} ;
static struct i2c_adapter oaktrail_hdmi_i2c_adapter = {
. name = " oaktrail_hdmi_i2c " ,
. nr = 3 ,
. owner = THIS_MODULE ,
. class = I2C_CLASS_DDC ,
. algo = & oaktrail_hdmi_i2c_algorithm ,
} ;
static void hdmi_i2c_read ( struct oaktrail_hdmi_dev * hdmi_dev )
{
struct hdmi_i2c_dev * i2c_dev = hdmi_dev - > i2c_dev ;
struct i2c_msg * msg = i2c_dev - > msg ;
u8 * buf = msg - > buf ;
u32 temp ;
int i , offset ;
offset = i2c_dev - > buf_offset ;
for ( i = 0 ; i < 0x10 ; i + + ) {
temp = HDMI_READ ( HDMI_HI2CRDB0 + ( i * 4 ) ) ;
memcpy ( buf + ( offset + i * 4 ) , & temp , 4 ) ;
}
i2c_dev - > buf_offset + = ( 0x10 * 4 ) ;
/* clearing read buffer full intr */
temp = HDMI_READ ( HDMI_HISR ) ;
HDMI_WRITE ( HDMI_HISR , temp | HDMI_INTR_I2C_FULL ) ;
HDMI_READ ( HDMI_HISR ) ;
/* continue read transaction */
temp = HDMI_READ ( HDMI_HI2CHCR ) ;
HDMI_WRITE ( HDMI_HI2CHCR , temp | HI2C_READ_CONTINUE ) ;
HDMI_READ ( HDMI_HI2CHCR ) ;
i2c_dev - > status = I2C_READ_DONE ;
return ;
}
static void hdmi_i2c_transaction_done ( struct oaktrail_hdmi_dev * hdmi_dev )
{
struct hdmi_i2c_dev * i2c_dev = hdmi_dev - > i2c_dev ;
u32 temp ;
/* clear transaction done intr */
temp = HDMI_READ ( HDMI_HISR ) ;
HDMI_WRITE ( HDMI_HISR , temp | HDMI_INTR_I2C_DONE ) ;
HDMI_READ ( HDMI_HISR ) ;
temp = HDMI_READ ( HDMI_HI2CHCR ) ;
HDMI_WRITE ( HDMI_HI2CHCR , temp & ~ HI2C_ENABLE_TRANSACTION ) ;
HDMI_READ ( HDMI_HI2CHCR ) ;
i2c_dev - > status = I2C_TRANSACTION_DONE ;
return ;
}
static irqreturn_t oaktrail_hdmi_i2c_handler ( int this_irq , void * dev )
{
struct oaktrail_hdmi_dev * hdmi_dev = dev ;
struct hdmi_i2c_dev * i2c_dev = hdmi_dev - > i2c_dev ;
u32 stat ;
stat = HDMI_READ ( HDMI_HISR ) ;
if ( stat & HDMI_INTR_HPD ) {
HDMI_WRITE ( HDMI_HISR , stat | HDMI_INTR_HPD ) ;
HDMI_READ ( HDMI_HISR ) ;
}
if ( stat & HDMI_INTR_I2C_FULL )
hdmi_i2c_read ( hdmi_dev ) ;
if ( stat & HDMI_INTR_I2C_DONE )
hdmi_i2c_transaction_done ( hdmi_dev ) ;
complete ( & i2c_dev - > complete ) ;
return IRQ_HANDLED ;
}
/*
* choose alternate function 2 of GPIO pin 52 , 53 ,
* which is used by HDMI I2C logic
*/
static void oaktrail_hdmi_i2c_gpio_fix ( void )
{
2012-05-03 15:09:22 +01:00
void __iomem * base ;
2011-11-03 18:22:26 +00:00
unsigned int gpio_base = 0xff12c000 ;
int gpio_len = 0x1000 ;
u32 temp ;
base = ioremap ( ( resource_size_t ) gpio_base , gpio_len ) ;
if ( base = = NULL ) {
DRM_ERROR ( " gpio ioremap fail \n " ) ;
return ;
}
temp = readl ( base + 0x44 ) ;
DRM_DEBUG_DRIVER ( " old gpio val %x \n " , temp ) ;
writel ( ( temp | 0x00000a00 ) , ( base + 0x44 ) ) ;
temp = readl ( base + 0x44 ) ;
DRM_DEBUG_DRIVER ( " new gpio val %x \n " , temp ) ;
iounmap ( base ) ;
}
int oaktrail_hdmi_i2c_init ( struct pci_dev * dev )
{
struct oaktrail_hdmi_dev * hdmi_dev ;
struct hdmi_i2c_dev * i2c_dev ;
int ret ;
hdmi_dev = pci_get_drvdata ( dev ) ;
i2c_dev = kzalloc ( sizeof ( struct hdmi_i2c_dev ) , GFP_KERNEL ) ;
if ( i2c_dev = = NULL ) {
DRM_ERROR ( " Can't allocate interface \n " ) ;
ret = - ENOMEM ;
goto exit ;
}
i2c_dev - > adap = & oaktrail_hdmi_i2c_adapter ;
i2c_dev - > status = I2C_STAT_INIT ;
init_completion ( & i2c_dev - > complete ) ;
mutex_init ( & i2c_dev - > i2c_lock ) ;
i2c_set_adapdata ( & oaktrail_hdmi_i2c_adapter , hdmi_dev ) ;
hdmi_dev - > i2c_dev = i2c_dev ;
/* Enable HDMI I2C function on gpio */
oaktrail_hdmi_i2c_gpio_fix ( ) ;
/* request irq */
ret = request_irq ( dev - > irq , oaktrail_hdmi_i2c_handler , IRQF_SHARED ,
oaktrail_hdmi_i2c_adapter . name , hdmi_dev ) ;
if ( ret ) {
DRM_ERROR ( " Failed to request IRQ for I2C controller \n " ) ;
goto err ;
}
/* Adapter registration */
ret = i2c_add_numbered_adapter ( & oaktrail_hdmi_i2c_adapter ) ;
return ret ;
err :
kfree ( i2c_dev ) ;
exit :
return ret ;
}
void oaktrail_hdmi_i2c_exit ( struct pci_dev * dev )
{
struct oaktrail_hdmi_dev * hdmi_dev ;
struct hdmi_i2c_dev * i2c_dev ;
hdmi_dev = pci_get_drvdata ( dev ) ;
2013-03-09 08:16:46 +00:00
i2c_del_adapter ( & oaktrail_hdmi_i2c_adapter ) ;
2011-11-03 18:22:26 +00:00
i2c_dev = hdmi_dev - > i2c_dev ;
kfree ( i2c_dev ) ;
free_irq ( dev - > irq , hdmi_dev ) ;
}