2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-12-05 18:41:28 +00:00
/*
* TDA9950 Consumer Electronics Control driver
*
* The NXP TDA9950 implements the HDMI Consumer Electronics Control
* interface . The host interface is similar to a mailbox : the data
* registers starting at REG_CDR0 are written to send a command to the
* internal CPU , and replies are read from these registers .
*
* As the data registers represent a mailbox , they must be accessed
* as a single I2C transaction . See the TDA9950 data sheet for details .
*/
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/platform_data/tda9950.h>
# include <linux/slab.h>
# include <drm/drm_edid.h>
# include <media/cec.h>
# include <media/cec-notifier.h>
enum {
REG_CSR = 0x00 ,
CSR_BUSY = BIT ( 7 ) ,
CSR_INT = BIT ( 6 ) ,
CSR_ERR = BIT ( 5 ) ,
REG_CER = 0x01 ,
REG_CVR = 0x02 ,
REG_CCR = 0x03 ,
CCR_RESET = BIT ( 7 ) ,
CCR_ON = BIT ( 6 ) ,
REG_ACKH = 0x04 ,
REG_ACKL = 0x05 ,
REG_CCONR = 0x06 ,
CCONR_ENABLE_ERROR = BIT ( 4 ) ,
CCONR_RETRY_MASK = 7 ,
REG_CDR0 = 0x07 ,
CDR1_REQ = 0x00 ,
CDR1_CNF = 0x01 ,
CDR1_IND = 0x81 ,
CDR1_ERR = 0x82 ,
CDR1_IER = 0x83 ,
CDR2_CNF_SUCCESS = 0x00 ,
CDR2_CNF_OFF_STATE = 0x80 ,
CDR2_CNF_BAD_REQ = 0x81 ,
CDR2_CNF_CEC_ACCESS = 0x82 ,
CDR2_CNF_ARB_ERROR = 0x83 ,
CDR2_CNF_BAD_TIMING = 0x84 ,
CDR2_CNF_NACK_ADDR = 0x85 ,
CDR2_CNF_NACK_DATA = 0x86 ,
} ;
struct tda9950_priv {
struct i2c_client * client ;
struct device * hdmi ;
struct cec_adapter * adap ;
struct tda9950_glue * glue ;
u16 addresses ;
struct cec_msg rx_msg ;
struct cec_notifier * notify ;
bool open ;
} ;
static int tda9950_write_range ( struct i2c_client * client , u8 addr , u8 * p , int cnt )
{
struct i2c_msg msg ;
2018-06-19 21:38:31 -07:00
u8 buf [ CEC_MAX_MSG_SIZE + 3 ] ;
2015-12-05 18:41:28 +00:00
int ret ;
2018-06-19 21:38:31 -07:00
if ( WARN_ON ( cnt > sizeof ( buf ) - 1 ) )
return - EINVAL ;
2015-12-05 18:41:28 +00:00
buf [ 0 ] = addr ;
memcpy ( buf + 1 , p , cnt ) ;
msg . addr = client - > addr ;
msg . flags = 0 ;
msg . len = cnt + 1 ;
msg . buf = buf ;
dev_dbg ( & client - > dev , " wr 0x%02x: %*ph \n " , addr , cnt , p ) ;
ret = i2c_transfer ( client - > adapter , & msg , 1 ) ;
if ( ret < 0 )
dev_err ( & client - > dev , " Error %d writing to cec:0x%x \n " , ret , addr ) ;
return ret < 0 ? ret : 0 ;
}
static void tda9950_write ( struct i2c_client * client , u8 addr , u8 val )
{
tda9950_write_range ( client , addr , & val , 1 ) ;
}
static int tda9950_read_range ( struct i2c_client * client , u8 addr , u8 * p , int cnt )
{
struct i2c_msg msg [ 2 ] ;
int ret ;
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = 0 ;
msg [ 0 ] . len = 1 ;
msg [ 0 ] . buf = & addr ;
msg [ 1 ] . addr = client - > addr ;
msg [ 1 ] . flags = I2C_M_RD ;
msg [ 1 ] . len = cnt ;
msg [ 1 ] . buf = p ;
ret = i2c_transfer ( client - > adapter , msg , 2 ) ;
if ( ret < 0 )
dev_err ( & client - > dev , " Error %d reading from cec:0x%x \n " , ret , addr ) ;
dev_dbg ( & client - > dev , " rd 0x%02x: %*ph \n " , addr , cnt , p ) ;
return ret ;
}
static u8 tda9950_read ( struct i2c_client * client , u8 addr )
{
int ret ;
u8 val ;
ret = tda9950_read_range ( client , addr , & val , 1 ) ;
if ( ret < 0 )
val = 0 ;
return val ;
}
static irqreturn_t tda9950_irq ( int irq , void * data )
{
struct tda9950_priv * priv = data ;
unsigned int tx_status ;
u8 csr , cconr , buf [ 19 ] ;
u8 arb_lost_cnt , nack_cnt , err_cnt ;
if ( ! priv - > open )
return IRQ_NONE ;
csr = tda9950_read ( priv - > client , REG_CSR ) ;
if ( ! ( csr & CSR_INT ) )
return IRQ_NONE ;
cconr = tda9950_read ( priv - > client , REG_CCONR ) & CCONR_RETRY_MASK ;
tda9950_read_range ( priv - > client , REG_CDR0 , buf , sizeof ( buf ) ) ;
/*
* This should never happen : the data sheet says that there will
* always be a valid message if the interrupt line is asserted .
*/
if ( buf [ 0 ] = = 0 ) {
dev_warn ( & priv - > client - > dev , " interrupt pending, but no message? \n " ) ;
return IRQ_NONE ;
}
switch ( buf [ 1 ] ) {
case CDR1_CNF : /* transmit result */
arb_lost_cnt = nack_cnt = err_cnt = 0 ;
switch ( buf [ 2 ] ) {
case CDR2_CNF_SUCCESS :
tx_status = CEC_TX_STATUS_OK ;
break ;
case CDR2_CNF_ARB_ERROR :
tx_status = CEC_TX_STATUS_ARB_LOST ;
arb_lost_cnt = cconr ;
break ;
case CDR2_CNF_NACK_ADDR :
tx_status = CEC_TX_STATUS_NACK ;
nack_cnt = cconr ;
break ;
default : /* some other error, refer to TDA9950 docs */
dev_err ( & priv - > client - > dev , " CNF reply error 0x%02x \n " ,
buf [ 2 ] ) ;
tx_status = CEC_TX_STATUS_ERROR ;
err_cnt = cconr ;
break ;
}
/* TDA9950 executes all retries for us */
2018-08-27 14:28:50 +02:00
if ( tx_status ! = CEC_TX_STATUS_OK )
tx_status | = CEC_TX_STATUS_MAX_RETRIES ;
2015-12-05 18:41:28 +00:00
cec_transmit_done ( priv - > adap , tx_status , arb_lost_cnt ,
nack_cnt , 0 , err_cnt ) ;
break ;
case CDR1_IND :
priv - > rx_msg . len = buf [ 0 ] - 2 ;
if ( priv - > rx_msg . len > CEC_MAX_MSG_SIZE )
priv - > rx_msg . len = CEC_MAX_MSG_SIZE ;
memcpy ( priv - > rx_msg . msg , buf + 2 , priv - > rx_msg . len ) ;
cec_received_msg ( priv - > adap , & priv - > rx_msg ) ;
break ;
default : /* unknown */
dev_err ( & priv - > client - > dev , " unknown service id 0x%02x \n " ,
buf [ 1 ] ) ;
break ;
}
return IRQ_HANDLED ;
}
static int tda9950_cec_transmit ( struct cec_adapter * adap , u8 attempts ,
u32 signal_free_time , struct cec_msg * msg )
{
struct tda9950_priv * priv = adap - > priv ;
u8 buf [ CEC_MAX_MSG_SIZE + 2 ] ;
buf [ 0 ] = 2 + msg - > len ;
buf [ 1 ] = CDR1_REQ ;
memcpy ( buf + 2 , msg - > msg , msg - > len ) ;
if ( attempts > 5 )
attempts = 5 ;
tda9950_write ( priv - > client , REG_CCONR , attempts ) ;
return tda9950_write_range ( priv - > client , REG_CDR0 , buf , 2 + msg - > len ) ;
}
static int tda9950_cec_adap_log_addr ( struct cec_adapter * adap , u8 addr )
{
struct tda9950_priv * priv = adap - > priv ;
u16 addresses ;
u8 buf [ 2 ] ;
if ( addr = = CEC_LOG_ADDR_INVALID )
addresses = priv - > addresses = 0 ;
else
addresses = priv - > addresses | = BIT ( addr ) ;
/* TDA9950 doesn't want address 15 set */
addresses & = 0x7fff ;
buf [ 0 ] = addresses > > 8 ;
buf [ 1 ] = addresses ;
return tda9950_write_range ( priv - > client , REG_ACKH , buf , 2 ) ;
}
/*
* When operating as part of the TDA998x , we need additional handling
* to initialise and shut down the TDA9950 part of the device . These
* two hooks are provided to allow the TDA998x code to perform those
* activities .
*/
static int tda9950_glue_open ( struct tda9950_priv * priv )
{
int ret = 0 ;
if ( priv - > glue & & priv - > glue - > open )
ret = priv - > glue - > open ( priv - > glue - > data ) ;
priv - > open = true ;
return ret ;
}
static void tda9950_glue_release ( struct tda9950_priv * priv )
{
priv - > open = false ;
if ( priv - > glue & & priv - > glue - > release )
priv - > glue - > release ( priv - > glue - > data ) ;
}
static int tda9950_open ( struct tda9950_priv * priv )
{
struct i2c_client * client = priv - > client ;
int ret ;
ret = tda9950_glue_open ( priv ) ;
if ( ret )
return ret ;
/* Reset the TDA9950, and wait 250ms for it to recover */
tda9950_write ( client , REG_CCR , CCR_RESET ) ;
msleep ( 250 ) ;
tda9950_cec_adap_log_addr ( priv - > adap , CEC_LOG_ADDR_INVALID ) ;
/* Start the command processor */
tda9950_write ( client , REG_CCR , CCR_ON ) ;
return 0 ;
}
static void tda9950_release ( struct tda9950_priv * priv )
{
struct i2c_client * client = priv - > client ;
int timeout = 50 ;
u8 csr ;
/* Stop the command processor */
tda9950_write ( client , REG_CCR , 0 ) ;
/* Wait up to .5s for it to signal non-busy */
do {
csr = tda9950_read ( client , REG_CSR ) ;
2018-05-27 22:42:55 +01:00
if ( ! ( csr & CSR_BUSY ) | | ! - - timeout )
2015-12-05 18:41:28 +00:00
break ;
msleep ( 10 ) ;
} while ( 1 ) ;
/* Warn the user that their IRQ may die if it's shared. */
if ( csr & CSR_BUSY )
dev_warn ( & client - > dev , " command processor failed to stop, irq%d may die (csr=0x%02x) \n " ,
client - > irq , csr ) ;
tda9950_glue_release ( priv ) ;
}
static int tda9950_cec_adap_enable ( struct cec_adapter * adap , bool enable )
{
struct tda9950_priv * priv = adap - > priv ;
if ( ! enable ) {
tda9950_release ( priv ) ;
return 0 ;
} else {
return tda9950_open ( priv ) ;
}
}
static const struct cec_adap_ops tda9950_cec_ops = {
. adap_enable = tda9950_cec_adap_enable ,
. adap_log_addr = tda9950_cec_adap_log_addr ,
. adap_transmit = tda9950_cec_transmit ,
} ;
/*
* When operating as part of the TDA998x , we need to claim additional
* resources . These two hooks permit the management of those resources .
*/
static void tda9950_devm_glue_exit ( void * data )
{
struct tda9950_glue * glue = data ;
if ( glue & & glue - > exit )
glue - > exit ( glue - > data ) ;
}
static int tda9950_devm_glue_init ( struct device * dev , struct tda9950_glue * glue )
{
int ret ;
if ( glue & & glue - > init ) {
ret = glue - > init ( glue - > data ) ;
if ( ret )
return ret ;
}
ret = devm_add_action ( dev , tda9950_devm_glue_exit , glue ) ;
if ( ret )
tda9950_devm_glue_exit ( glue ) ;
return ret ;
}
static void tda9950_cec_del ( void * data )
{
struct tda9950_priv * priv = data ;
cec_delete_adapter ( priv - > adap ) ;
}
static int tda9950_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct tda9950_glue * glue = client - > dev . platform_data ;
struct device * dev = & client - > dev ;
struct tda9950_priv * priv ;
unsigned long irqflags ;
int ret ;
u8 cvr ;
/*
* We must have I2C functionality : our multi - byte accesses
* must be performed as a single contiguous transaction .
*/
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( & client - > dev ,
" adapter does not support I2C functionality \n " ) ;
return - ENXIO ;
}
/* We must have an interrupt to be functional. */
if ( client - > irq < = 0 ) {
dev_err ( & client - > dev , " driver requires an interrupt \n " ) ;
return - ENXIO ;
}
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > client = client ;
priv - > glue = glue ;
i2c_set_clientdata ( client , priv ) ;
/*
* If we ' re part of a TDA998x , we want the class devices to be
* associated with the HDMI Tx so we have a tight relationship
* between the HDMI interface and the CEC interface .
*/
priv - > hdmi = dev ;
if ( glue & & glue - > parent )
priv - > hdmi = glue - > parent ;
priv - > adap = cec_allocate_adapter ( & tda9950_cec_ops , priv , " tda9950 " ,
CEC_CAP_DEFAULTS ,
CEC_MAX_LOG_ADDRS ) ;
if ( IS_ERR ( priv - > adap ) )
return PTR_ERR ( priv - > adap ) ;
ret = devm_add_action ( dev , tda9950_cec_del , priv ) ;
if ( ret ) {
cec_delete_adapter ( priv - > adap ) ;
return ret ;
}
ret = tda9950_devm_glue_init ( dev , glue ) ;
if ( ret )
return ret ;
ret = tda9950_glue_open ( priv ) ;
if ( ret )
return ret ;
cvr = tda9950_read ( client , REG_CVR ) ;
dev_info ( & client - > dev ,
" TDA9950 CEC interface, hardware version %u.%u \n " ,
cvr > > 4 , cvr & 15 ) ;
tda9950_glue_release ( priv ) ;
irqflags = IRQF_TRIGGER_FALLING ;
if ( glue )
irqflags = glue - > irq_flags ;
ret = devm_request_threaded_irq ( dev , client - > irq , NULL , tda9950_irq ,
irqflags | IRQF_SHARED | IRQF_ONESHOT ,
dev_name ( & client - > dev ) , priv ) ;
if ( ret < 0 )
return ret ;
priv - > notify = cec_notifier_get ( priv - > hdmi ) ;
if ( ! priv - > notify )
return - ENOMEM ;
ret = cec_register_adapter ( priv - > adap , priv - > hdmi ) ;
if ( ret < 0 ) {
cec_notifier_put ( priv - > notify ) ;
return ret ;
}
/*
* CEC documentation says we must not call cec_delete_adapter
* after a successful call to cec_register_adapter ( ) .
*/
devm_remove_action ( dev , tda9950_cec_del , priv ) ;
cec_register_cec_notifier ( priv - > adap , priv - > notify ) ;
return 0 ;
}
static int tda9950_remove ( struct i2c_client * client )
{
struct tda9950_priv * priv = i2c_get_clientdata ( client ) ;
cec_unregister_adapter ( priv - > adap ) ;
cec_notifier_put ( priv - > notify ) ;
return 0 ;
}
static struct i2c_device_id tda9950_ids [ ] = {
{ " tda9950 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , tda9950_ids ) ;
static struct i2c_driver tda9950_driver = {
. probe = tda9950_probe ,
. remove = tda9950_remove ,
. driver = {
. name = " tda9950 " ,
} ,
. id_table = tda9950_ids ,
} ;
module_i2c_driver ( tda9950_driver ) ;
MODULE_AUTHOR ( " Russell King <rmk+kernel@armlinux.org.uk> " ) ;
MODULE_DESCRIPTION ( " TDA9950/TDA998x Consumer Electronics Control Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;