2009-04-08 03:16:42 +04:00
/*
* Copyright © 2009 Keith Packard
*
* Permission to use , copy , modify , distribute , and sell this software and its
* documentation for any purpose is hereby granted without fee , provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation , and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific ,
* written prior permission . The copyright holders make no representations
* about the suitability of this software for any purpose . It is provided " as
* is " without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE ,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS , IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL , INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE ,
* DATA OR PROFITS , WHETHER IN AN ACTION OF CONTRACT , NEGLIGENCE OR OTHER
* TORTIOUS ACTION , ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/i2c.h>
2012-10-02 21:01:07 +04:00
# include <drm/drm_dp_helper.h>
# include <drm/drmP.h>
2009-04-08 03:16:42 +04:00
2012-11-01 17:45:18 +04:00
/**
* DOC : dp helpers
*
* These functions contain some common logic and helpers at various abstraction
* levels to deal with Display Port sink devices and related things like DP aux
* channel transfers , EDID reading over DP aux channels , decoding certain DPCD
* blocks , . . .
*/
2009-04-08 03:16:42 +04:00
/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
static int
i2c_algo_dp_aux_transaction ( struct i2c_adapter * adapter , int mode ,
uint8_t write_byte , uint8_t * read_byte )
{
struct i2c_algo_dp_aux_data * algo_data = adapter - > algo_data ;
int ret ;
2012-10-18 12:15:23 +04:00
2009-12-04 03:55:24 +03:00
ret = ( * algo_data - > aux_ch ) ( adapter , mode ,
write_byte , read_byte ) ;
return ret ;
2009-04-08 03:16:42 +04:00
}
/*
* I2C over AUX CH
*/
/*
* Send the address . If the I2C link is running , this ' restarts '
* the connection with the new address , this is used for doing
* a write followed by a read ( as needed for DDC )
*/
static int
i2c_algo_dp_aux_address ( struct i2c_adapter * adapter , u16 address , bool reading )
{
struct i2c_algo_dp_aux_data * algo_data = adapter - > algo_data ;
int mode = MODE_I2C_START ;
int ret ;
if ( reading )
mode | = MODE_I2C_READ ;
else
mode | = MODE_I2C_WRITE ;
algo_data - > address = address ;
algo_data - > running = true ;
ret = i2c_algo_dp_aux_transaction ( adapter , mode , 0 , NULL ) ;
return ret ;
}
/*
* Stop the I2C transaction . This closes out the link , sending
* a bare address packet with the MOT bit turned off
*/
static void
i2c_algo_dp_aux_stop ( struct i2c_adapter * adapter , bool reading )
{
struct i2c_algo_dp_aux_data * algo_data = adapter - > algo_data ;
int mode = MODE_I2C_STOP ;
if ( reading )
mode | = MODE_I2C_READ ;
else
mode | = MODE_I2C_WRITE ;
if ( algo_data - > running ) {
( void ) i2c_algo_dp_aux_transaction ( adapter , mode , 0 , NULL ) ;
algo_data - > running = false ;
}
}
/*
* Write a single byte to the current I2C address , the
* the I2C link must be running or this returns - EIO
*/
static int
i2c_algo_dp_aux_put_byte ( struct i2c_adapter * adapter , u8 byte )
{
struct i2c_algo_dp_aux_data * algo_data = adapter - > algo_data ;
int ret ;
if ( ! algo_data - > running )
return - EIO ;
ret = i2c_algo_dp_aux_transaction ( adapter , MODE_I2C_WRITE , byte , NULL ) ;
return ret ;
}
/*
* Read a single byte from the current I2C address , the
* I2C link must be running or this returns - EIO
*/
static int
i2c_algo_dp_aux_get_byte ( struct i2c_adapter * adapter , u8 * byte_ret )
{
struct i2c_algo_dp_aux_data * algo_data = adapter - > algo_data ;
int ret ;
if ( ! algo_data - > running )
return - EIO ;
ret = i2c_algo_dp_aux_transaction ( adapter , MODE_I2C_READ , 0 , byte_ret ) ;
return ret ;
}
static int
i2c_algo_dp_aux_xfer ( struct i2c_adapter * adapter ,
struct i2c_msg * msgs ,
int num )
{
int ret = 0 ;
bool reading = false ;
int m ;
int b ;
for ( m = 0 ; m < num ; m + + ) {
u16 len = msgs [ m ] . len ;
u8 * buf = msgs [ m ] . buf ;
reading = ( msgs [ m ] . flags & I2C_M_RD ) ! = 0 ;
ret = i2c_algo_dp_aux_address ( adapter , msgs [ m ] . addr , reading ) ;
if ( ret < 0 )
break ;
if ( reading ) {
for ( b = 0 ; b < len ; b + + ) {
ret = i2c_algo_dp_aux_get_byte ( adapter , & buf [ b ] ) ;
if ( ret < 0 )
break ;
}
} else {
for ( b = 0 ; b < len ; b + + ) {
ret = i2c_algo_dp_aux_put_byte ( adapter , buf [ b ] ) ;
if ( ret < 0 )
break ;
}
}
if ( ret < 0 )
break ;
}
if ( ret > = 0 )
ret = num ;
i2c_algo_dp_aux_stop ( adapter , reading ) ;
2009-10-09 07:39:41 +04:00
DRM_DEBUG_KMS ( " dp_aux_xfer return %d \n " , ret ) ;
2009-04-08 03:16:42 +04:00
return ret ;
}
static u32
i2c_algo_dp_aux_functionality ( struct i2c_adapter * adapter )
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_10BIT_ADDR ;
}
static const struct i2c_algorithm i2c_dp_aux_algo = {
. master_xfer = i2c_algo_dp_aux_xfer ,
. functionality = i2c_algo_dp_aux_functionality ,
} ;
static void
i2c_dp_aux_reset_bus ( struct i2c_adapter * adapter )
{
( void ) i2c_algo_dp_aux_address ( adapter , 0 , false ) ;
( void ) i2c_algo_dp_aux_stop ( adapter , false ) ;
}
static int
i2c_dp_aux_prepare_bus ( struct i2c_adapter * adapter )
{
adapter - > algo = & i2c_dp_aux_algo ;
adapter - > retries = 3 ;
i2c_dp_aux_reset_bus ( adapter ) ;
return 0 ;
}
2012-11-01 17:45:18 +04:00
/**
* i2c_dp_aux_add_bus ( ) - register an i2c adapter using the aux ch helper
* @ adapter : i2c adapter to register
*
2014-04-29 13:44:37 +04:00
* This registers an i2c adapter that uses dp aux channel as it ' s underlaying
2012-11-01 17:45:18 +04:00
* transport . The driver needs to fill out the & i2c_algo_dp_aux_data structure
* and store it in the algo_data member of the @ adapter argument . This will be
* used by the i2c over dp aux algorithm to drive the hardware .
*
* RETURNS :
* 0 on success , - ERRNO on failure .
2014-05-07 12:23:55 +04:00
*
* IMPORTANT :
* This interface is deprecated , please switch to the new dp aux helpers and
2014-06-04 10:02:28 +04:00
* drm_dp_aux_register ( ) .
2012-11-01 17:45:18 +04:00
*/
2009-04-08 03:16:42 +04:00
int
i2c_dp_aux_add_bus ( struct i2c_adapter * adapter )
{
int error ;
2012-10-18 12:15:23 +04:00
2009-04-08 03:16:42 +04:00
error = i2c_dp_aux_prepare_bus ( adapter ) ;
if ( error )
return error ;
error = i2c_add_adapter ( adapter ) ;
return error ;
}
EXPORT_SYMBOL ( i2c_dp_aux_add_bus ) ;
2012-10-18 12:15:24 +04:00
/* Helpers for DP link training */
2013-09-27 20:01:01 +04:00
static u8 dp_link_status ( const u8 link_status [ DP_LINK_STATUS_SIZE ] , int r )
2012-10-18 12:15:24 +04:00
{
return link_status [ r - DP_LANE0_1_STATUS ] ;
}
2013-09-27 20:01:01 +04:00
static u8 dp_get_lane_status ( const u8 link_status [ DP_LINK_STATUS_SIZE ] ,
2012-10-18 12:15:24 +04:00
int lane )
{
int i = DP_LANE0_1_STATUS + ( lane > > 1 ) ;
int s = ( lane & 1 ) * 4 ;
u8 l = dp_link_status ( link_status , i ) ;
return ( l > > s ) & 0xf ;
}
2013-09-27 20:01:01 +04:00
bool drm_dp_channel_eq_ok ( const u8 link_status [ DP_LINK_STATUS_SIZE ] ,
2012-10-18 12:15:24 +04:00
int lane_count )
{
u8 lane_align ;
u8 lane_status ;
int lane ;
lane_align = dp_link_status ( link_status ,
DP_LANE_ALIGN_STATUS_UPDATED ) ;
if ( ( lane_align & DP_INTERLANE_ALIGN_DONE ) = = 0 )
return false ;
for ( lane = 0 ; lane < lane_count ; lane + + ) {
lane_status = dp_get_lane_status ( link_status , lane ) ;
if ( ( lane_status & DP_CHANNEL_EQ_BITS ) ! = DP_CHANNEL_EQ_BITS )
return false ;
}
return true ;
}
EXPORT_SYMBOL ( drm_dp_channel_eq_ok ) ;
2013-09-27 20:01:01 +04:00
bool drm_dp_clock_recovery_ok ( const u8 link_status [ DP_LINK_STATUS_SIZE ] ,
2012-10-18 12:15:24 +04:00
int lane_count )
{
int lane ;
u8 lane_status ;
for ( lane = 0 ; lane < lane_count ; lane + + ) {
lane_status = dp_get_lane_status ( link_status , lane ) ;
if ( ( lane_status & DP_LANE_CR_DONE ) = = 0 )
return false ;
}
return true ;
}
EXPORT_SYMBOL ( drm_dp_clock_recovery_ok ) ;
2012-10-18 12:15:27 +04:00
2013-09-27 20:01:01 +04:00
u8 drm_dp_get_adjust_request_voltage ( const u8 link_status [ DP_LINK_STATUS_SIZE ] ,
2012-10-18 12:15:27 +04:00
int lane )
{
int i = DP_ADJUST_REQUEST_LANE0_1 + ( lane > > 1 ) ;
int s = ( ( lane & 1 ) ?
DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT ) ;
u8 l = dp_link_status ( link_status , i ) ;
return ( ( l > > s ) & 0x3 ) < < DP_TRAIN_VOLTAGE_SWING_SHIFT ;
}
EXPORT_SYMBOL ( drm_dp_get_adjust_request_voltage ) ;
2013-09-27 20:01:01 +04:00
u8 drm_dp_get_adjust_request_pre_emphasis ( const u8 link_status [ DP_LINK_STATUS_SIZE ] ,
2012-10-18 12:15:27 +04:00
int lane )
{
int i = DP_ADJUST_REQUEST_LANE0_1 + ( lane > > 1 ) ;
int s = ( ( lane & 1 ) ?
DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT ) ;
u8 l = dp_link_status ( link_status , i ) ;
return ( ( l > > s ) & 0x3 ) < < DP_TRAIN_PRE_EMPHASIS_SHIFT ;
}
EXPORT_SYMBOL ( drm_dp_get_adjust_request_pre_emphasis ) ;
2013-09-27 20:01:01 +04:00
void drm_dp_link_train_clock_recovery_delay ( const u8 dpcd [ DP_RECEIVER_CAP_SIZE ] ) {
2012-10-18 17:32:40 +04:00
if ( dpcd [ DP_TRAINING_AUX_RD_INTERVAL ] = = 0 )
udelay ( 100 ) ;
else
mdelay ( dpcd [ DP_TRAINING_AUX_RD_INTERVAL ] * 4 ) ;
}
EXPORT_SYMBOL ( drm_dp_link_train_clock_recovery_delay ) ;
2013-09-27 20:01:01 +04:00
void drm_dp_link_train_channel_eq_delay ( const u8 dpcd [ DP_RECEIVER_CAP_SIZE ] ) {
2012-10-18 17:32:40 +04:00
if ( dpcd [ DP_TRAINING_AUX_RD_INTERVAL ] = = 0 )
udelay ( 400 ) ;
else
mdelay ( dpcd [ DP_TRAINING_AUX_RD_INTERVAL ] * 4 ) ;
}
EXPORT_SYMBOL ( drm_dp_link_train_channel_eq_delay ) ;
2012-10-18 12:15:31 +04:00
u8 drm_dp_link_rate_to_bw_code ( int link_rate )
{
switch ( link_rate ) {
case 162000 :
default :
return DP_LINK_BW_1_62 ;
case 270000 :
return DP_LINK_BW_2_7 ;
case 540000 :
return DP_LINK_BW_5_4 ;
}
}
EXPORT_SYMBOL ( drm_dp_link_rate_to_bw_code ) ;
int drm_dp_bw_code_to_link_rate ( u8 link_bw )
{
switch ( link_bw ) {
case DP_LINK_BW_1_62 :
default :
return 162000 ;
case DP_LINK_BW_2_7 :
return 270000 ;
case DP_LINK_BW_5_4 :
return 540000 ;
}
}
EXPORT_SYMBOL ( drm_dp_bw_code_to_link_rate ) ;
2013-11-28 14:31:00 +04:00
/**
* DOC : dp helpers
*
* The DisplayPort AUX channel is an abstraction to allow generic , driver -
* independent access to AUX functionality . Drivers can take advantage of
* this by filling in the fields of the drm_dp_aux structure .
*
* Transactions are described using a hardware - independent drm_dp_aux_msg
* structure , which is passed into a driver ' s . transfer ( ) implementation .
* Both native and I2C - over - AUX transactions are supported .
*/
static int drm_dp_dpcd_access ( struct drm_dp_aux * aux , u8 request ,
unsigned int offset , void * buffer , size_t size )
{
struct drm_dp_aux_msg msg ;
unsigned int retry ;
int err ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . address = offset ;
msg . request = request ;
msg . buffer = buffer ;
msg . size = size ;
/*
* The specification doesn ' t give any recommendation on how often to
* retry native transactions , so retry 7 times like for I2C - over - AUX
* transactions .
*/
for ( retry = 0 ; retry < 7 ; retry + + ) {
2014-06-04 10:02:28 +04:00
mutex_lock ( & aux - > hw_mutex ) ;
2013-11-28 14:31:00 +04:00
err = aux - > transfer ( aux , & msg ) ;
2014-06-04 10:02:28 +04:00
mutex_unlock ( & aux - > hw_mutex ) ;
2013-11-28 14:31:00 +04:00
if ( err < 0 ) {
if ( err = = - EBUSY )
continue ;
return err ;
}
switch ( msg . reply & DP_AUX_NATIVE_REPLY_MASK ) {
case DP_AUX_NATIVE_REPLY_ACK :
2014-04-04 05:34:37 +04:00
if ( err < size )
return - EPROTO ;
2013-11-28 14:31:00 +04:00
return err ;
case DP_AUX_NATIVE_REPLY_NACK :
return - EIO ;
case DP_AUX_NATIVE_REPLY_DEFER :
usleep_range ( 400 , 500 ) ;
break ;
}
}
2014-03-21 18:34:06 +04:00
DRM_DEBUG_KMS ( " too many retries, giving up \n " ) ;
2013-11-28 14:31:00 +04:00
return - EIO ;
}
/**
* drm_dp_dpcd_read ( ) - read a series of bytes from the DPCD
* @ aux : DisplayPort AUX channel
* @ offset : address of the ( first ) register to read
* @ buffer : buffer to store the register values
* @ size : number of bytes in @ buffer
*
* Returns the number of bytes transferred on success , or a negative error
* code on failure . - EIO is returned if the request was NAKed by the sink or
* if the retry count was exceeded . If not all bytes were transferred , this
* function returns - EPROTO . Errors from the underlying AUX channel transfer
* function , with the exception of - EBUSY ( which causes the transaction to
* be retried ) , are propagated to the caller .
*/
ssize_t drm_dp_dpcd_read ( struct drm_dp_aux * aux , unsigned int offset ,
void * buffer , size_t size )
{
return drm_dp_dpcd_access ( aux , DP_AUX_NATIVE_READ , offset , buffer ,
size ) ;
}
EXPORT_SYMBOL ( drm_dp_dpcd_read ) ;
/**
* drm_dp_dpcd_write ( ) - write a series of bytes to the DPCD
* @ aux : DisplayPort AUX channel
* @ offset : address of the ( first ) register to write
* @ buffer : buffer containing the values to write
* @ size : number of bytes in @ buffer
*
* Returns the number of bytes transferred on success , or a negative error
* code on failure . - EIO is returned if the request was NAKed by the sink or
* if the retry count was exceeded . If not all bytes were transferred , this
* function returns - EPROTO . Errors from the underlying AUX channel transfer
* function , with the exception of - EBUSY ( which causes the transaction to
* be retried ) , are propagated to the caller .
*/
ssize_t drm_dp_dpcd_write ( struct drm_dp_aux * aux , unsigned int offset ,
void * buffer , size_t size )
{
return drm_dp_dpcd_access ( aux , DP_AUX_NATIVE_WRITE , offset , buffer ,
size ) ;
}
EXPORT_SYMBOL ( drm_dp_dpcd_write ) ;
2013-11-22 19:37:57 +04:00
/**
* drm_dp_dpcd_read_link_status ( ) - read DPCD link status ( bytes 0x202 - 0x207 )
* @ aux : DisplayPort AUX channel
* @ status : buffer to store the link status in ( must be at least 6 bytes )
*
* Returns the number of bytes transferred on success or a negative error
* code on failure .
*/
int drm_dp_dpcd_read_link_status ( struct drm_dp_aux * aux ,
u8 status [ DP_LINK_STATUS_SIZE ] )
{
return drm_dp_dpcd_read ( aux , DP_LANE0_1_STATUS , status ,
DP_LINK_STATUS_SIZE ) ;
}
EXPORT_SYMBOL ( drm_dp_dpcd_read_link_status ) ;
2013-12-09 14:47:55 +04:00
/**
* drm_dp_link_probe ( ) - probe a DisplayPort link for capabilities
* @ aux : DisplayPort AUX channel
* @ link : pointer to structure in which to return link capabilities
*
* The structure filled in by this function can usually be passed directly
* into drm_dp_link_power_up ( ) and drm_dp_link_configure ( ) to power up and
* configure the link based on the link ' s capabilities .
*
* Returns 0 on success or a negative error code on failure .
*/
int drm_dp_link_probe ( struct drm_dp_aux * aux , struct drm_dp_link * link )
{
u8 values [ 3 ] ;
int err ;
memset ( link , 0 , sizeof ( * link ) ) ;
err = drm_dp_dpcd_read ( aux , DP_DPCD_REV , values , sizeof ( values ) ) ;
if ( err < 0 )
return err ;
link - > revision = values [ 0 ] ;
link - > rate = drm_dp_bw_code_to_link_rate ( values [ 1 ] ) ;
link - > num_lanes = values [ 2 ] & DP_MAX_LANE_COUNT_MASK ;
if ( values [ 2 ] & DP_ENHANCED_FRAME_CAP )
link - > capabilities | = DP_LINK_CAP_ENHANCED_FRAMING ;
return 0 ;
}
EXPORT_SYMBOL ( drm_dp_link_probe ) ;
/**
* drm_dp_link_power_up ( ) - power up a DisplayPort link
* @ aux : DisplayPort AUX channel
* @ link : pointer to a structure containing the link configuration
*
* Returns 0 on success or a negative error code on failure .
*/
int drm_dp_link_power_up ( struct drm_dp_aux * aux , struct drm_dp_link * link )
{
u8 value ;
int err ;
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
if ( link - > revision < 0x11 )
return 0 ;
err = drm_dp_dpcd_readb ( aux , DP_SET_POWER , & value ) ;
if ( err < 0 )
return err ;
value & = ~ DP_SET_POWER_MASK ;
value | = DP_SET_POWER_D0 ;
err = drm_dp_dpcd_writeb ( aux , DP_SET_POWER , value ) ;
if ( err < 0 )
return err ;
/*
* According to the DP 1.1 specification , a " Sink Device must exit the
* power saving state within 1 ms " (Section 2.5.3.1, Table 5-52, " Sink
* Control Field " (register 0x600).
*/
usleep_range ( 1000 , 2000 ) ;
return 0 ;
}
EXPORT_SYMBOL ( drm_dp_link_power_up ) ;
/**
* drm_dp_link_configure ( ) - configure a DisplayPort link
* @ aux : DisplayPort AUX channel
* @ link : pointer to a structure containing the link configuration
*
* Returns 0 on success or a negative error code on failure .
*/
int drm_dp_link_configure ( struct drm_dp_aux * aux , struct drm_dp_link * link )
{
u8 values [ 2 ] ;
int err ;
values [ 0 ] = drm_dp_link_rate_to_bw_code ( link - > rate ) ;
values [ 1 ] = link - > num_lanes ;
if ( link - > capabilities & DP_LINK_CAP_ENHANCED_FRAMING )
values [ 1 ] | = DP_LANE_COUNT_ENHANCED_FRAME_EN ;
err = drm_dp_dpcd_write ( aux , DP_LINK_BW_SET , values , sizeof ( values ) ) ;
if ( err < 0 )
return err ;
return 0 ;
}
EXPORT_SYMBOL ( drm_dp_link_configure ) ;
2013-12-12 12:57:53 +04:00
/*
* I2C - over - AUX implementation
*/
static u32 drm_dp_i2c_functionality ( struct i2c_adapter * adapter )
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_10BIT_ADDR ;
}
/*
* Transfer a single I2C - over - AUX message and handle various error conditions ,
2014-04-07 18:33:45 +04:00
* retrying the transaction as appropriate . It is assumed that the
* aux - > transfer function does not modify anything in the msg other than the
* reply field .
2013-12-12 12:57:53 +04:00
*/
static int drm_dp_i2c_do_msg ( struct drm_dp_aux * aux , struct drm_dp_aux_msg * msg )
{
unsigned int retry ;
int err ;
/*
* DP1 .2 sections 2.7 .7 .1 .5 .6 .1 and 2.7 .7 .1 .6 .6 .1 : A DP Source device
* is required to retry at least seven times upon receiving AUX_DEFER
* before giving up the AUX transaction .
*/
for ( retry = 0 ; retry < 7 ; retry + + ) {
2014-06-04 10:02:28 +04:00
mutex_lock ( & aux - > hw_mutex ) ;
2013-12-12 12:57:53 +04:00
err = aux - > transfer ( aux , msg ) ;
2014-06-04 10:02:28 +04:00
mutex_unlock ( & aux - > hw_mutex ) ;
2013-12-12 12:57:53 +04:00
if ( err < 0 ) {
if ( err = = - EBUSY )
continue ;
DRM_DEBUG_KMS ( " transaction failed: %d \n " , err ) ;
return err ;
}
switch ( msg - > reply & DP_AUX_NATIVE_REPLY_MASK ) {
case DP_AUX_NATIVE_REPLY_ACK :
/*
* For I2C - over - AUX transactions this isn ' t enough , we
* need to check for the I2C ACK reply .
*/
break ;
case DP_AUX_NATIVE_REPLY_NACK :
DRM_DEBUG_KMS ( " native nack \n " ) ;
return - EREMOTEIO ;
case DP_AUX_NATIVE_REPLY_DEFER :
DRM_DEBUG_KMS ( " native defer " ) ;
/*
* We could check for I2C bit rate capabilities and if
* available adjust this interval . We could also be
* more careful with DP - to - legacy adapters where a
* long legacy cable may force very low I2C bit rates .
*
* For now just defer for long enough to hopefully be
* safe for all use - cases .
*/
usleep_range ( 500 , 600 ) ;
continue ;
default :
DRM_ERROR ( " invalid native reply %#04x \n " , msg - > reply ) ;
return - EREMOTEIO ;
}
switch ( msg - > reply & DP_AUX_I2C_REPLY_MASK ) {
case DP_AUX_I2C_REPLY_ACK :
/*
* Both native ACK and I2C ACK replies received . We
* can assume the transfer was successful .
*/
2014-04-04 05:34:37 +04:00
if ( err < msg - > size )
return - EPROTO ;
2013-12-12 12:57:53 +04:00
return 0 ;
case DP_AUX_I2C_REPLY_NACK :
DRM_DEBUG_KMS ( " I2C nack \n " ) ;
return - EREMOTEIO ;
case DP_AUX_I2C_REPLY_DEFER :
DRM_DEBUG_KMS ( " I2C defer \n " ) ;
usleep_range ( 400 , 500 ) ;
continue ;
default :
DRM_ERROR ( " invalid I2C reply %#04x \n " , msg - > reply ) ;
return - EREMOTEIO ;
}
}
2014-03-21 18:34:06 +04:00
DRM_DEBUG_KMS ( " too many retries, giving up \n " ) ;
2013-12-12 12:57:53 +04:00
return - EREMOTEIO ;
}
static int drm_dp_i2c_xfer ( struct i2c_adapter * adapter , struct i2c_msg * msgs ,
int num )
{
struct drm_dp_aux * aux = adapter - > algo_data ;
unsigned int i , j ;
2014-04-07 18:33:44 +04:00
struct drm_dp_aux_msg msg ;
int err = 0 ;
2013-12-12 12:57:53 +04:00
2014-04-07 18:33:44 +04:00
memset ( & msg , 0 , sizeof ( msg ) ) ;
2013-12-12 12:57:53 +04:00
2014-04-07 18:33:44 +04:00
for ( i = 0 ; i < num ; i + + ) {
msg . address = msgs [ i ] . addr ;
msg . request = ( msgs [ i ] . flags & I2C_M_RD ) ?
DP_AUX_I2C_READ :
DP_AUX_I2C_WRITE ;
msg . request | = DP_AUX_I2C_MOT ;
/* Send a bare address packet to start the transaction.
* Zero sized messages specify an address only ( bare
* address ) transaction .
*/
msg . buffer = NULL ;
msg . size = 0 ;
err = drm_dp_i2c_do_msg ( aux , & msg ) ;
if ( err < 0 )
break ;
2013-12-12 12:57:53 +04:00
/*
* Many hardware implementations support FIFOs larger than a
* single byte , but it has been empirically determined that
* transferring data in larger chunks can actually lead to
* decreased performance . Therefore each message is simply
* transferred byte - by - byte .
*/
for ( j = 0 ; j < msgs [ i ] . len ; j + + ) {
msg . buffer = msgs [ i ] . buf + j ;
msg . size = 1 ;
err = drm_dp_i2c_do_msg ( aux , & msg ) ;
if ( err < 0 )
2014-04-07 18:33:44 +04:00
break ;
2013-12-12 12:57:53 +04:00
}
2014-04-07 18:33:44 +04:00
if ( err < 0 )
break ;
2013-12-12 12:57:53 +04:00
}
2014-04-07 18:33:44 +04:00
if ( err > = 0 )
err = num ;
/* Send a bare address packet to close out the transaction.
* Zero sized messages specify an address only ( bare
* address ) transaction .
*/
msg . request & = ~ DP_AUX_I2C_MOT ;
msg . buffer = NULL ;
msg . size = 0 ;
( void ) drm_dp_i2c_do_msg ( aux , & msg ) ;
2013-12-12 12:57:53 +04:00
2014-04-07 18:33:44 +04:00
return err ;
2013-12-12 12:57:53 +04:00
}
static const struct i2c_algorithm drm_dp_i2c_algo = {
. functionality = drm_dp_i2c_functionality ,
. master_xfer = drm_dp_i2c_xfer ,
} ;
/**
2014-06-04 10:02:28 +04:00
* drm_dp_aux_register ( ) - initialise and register aux channel
2013-12-12 12:57:53 +04:00
* @ aux : DisplayPort AUX channel
*
* Returns 0 on success or a negative error code on failure .
*/
2014-06-04 10:02:28 +04:00
int drm_dp_aux_register ( struct drm_dp_aux * aux )
2013-12-12 12:57:53 +04:00
{
2014-06-04 10:02:28 +04:00
mutex_init ( & aux - > hw_mutex ) ;
2013-12-12 12:57:53 +04:00
aux - > ddc . algo = & drm_dp_i2c_algo ;
aux - > ddc . algo_data = aux ;
aux - > ddc . retries = 3 ;
aux - > ddc . class = I2C_CLASS_DDC ;
aux - > ddc . owner = THIS_MODULE ;
aux - > ddc . dev . parent = aux - > dev ;
aux - > ddc . dev . of_node = aux - > dev - > of_node ;
2014-03-14 18:51:12 +04:00
strlcpy ( aux - > ddc . name , aux - > name ? aux - > name : dev_name ( aux - > dev ) ,
sizeof ( aux - > ddc . name ) ) ;
2013-12-12 12:57:53 +04:00
return i2c_add_adapter ( & aux - > ddc ) ;
}
2014-06-04 10:02:28 +04:00
EXPORT_SYMBOL ( drm_dp_aux_register ) ;
2013-12-12 12:57:53 +04:00
/**
2014-06-04 10:02:28 +04:00
* drm_dp_aux_unregister ( ) - unregister an AUX adapter
2013-12-12 12:57:53 +04:00
* @ aux : DisplayPort AUX channel
*/
2014-06-04 10:02:28 +04:00
void drm_dp_aux_unregister ( struct drm_dp_aux * aux )
2013-12-12 12:57:53 +04:00
{
i2c_del_adapter ( & aux - > ddc ) ;
}
2014-06-04 10:02:28 +04:00
EXPORT_SYMBOL ( drm_dp_aux_unregister ) ;