2014-09-30 08:01:47 +04:00
/*
* Panasonic MN88473 DVB - T / T2 / C demodulator driver
*
* Copyright ( C ) 2014 Antti Palosaari < crope @ iki . fi >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that 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 .
*/
# include "mn88473_priv.h"
static int mn88473_get_tune_settings ( struct dvb_frontend * fe ,
2015-02-19 05:05:40 +03:00
struct dvb_frontend_tune_settings * s )
2014-09-30 08:01:47 +04:00
{
s - > min_delay_ms = 1000 ;
return 0 ;
}
static int mn88473_set_frontend ( struct dvb_frontend * fe )
{
2014-10-03 05:41:26 +04:00
struct i2c_client * client = fe - > demodulator_priv ;
struct mn88473_dev * dev = i2c_get_clientdata ( client ) ;
2014-09-30 08:01:47 +04:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2014-10-01 00:52:03 +04:00
int ret , i ;
2016-02-04 22:01:51 +03:00
unsigned int uitmp ;
2014-10-01 06:19:30 +04:00
u32 if_frequency ;
2016-02-04 22:01:51 +03:00
u8 delivery_system_val , if_val [ 3 ] , * conf_val_ptr ;
u8 reg_bank2_2d_val , reg_bank0_d2_val ;
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev ,
2015-02-19 05:05:40 +03:00
" delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d \n " ,
2016-02-04 22:01:51 +03:00
c - > delivery_system , c - > modulation , c - > frequency ,
c - > bandwidth_hz , c - > symbol_rate , c - > inversion , c - > stream_id ) ;
if ( ! dev - > active ) {
2014-09-30 08:01:47 +04:00
ret = - EAGAIN ;
goto err ;
}
2014-10-01 00:52:03 +04:00
switch ( c - > delivery_system ) {
2014-10-01 02:56:22 +04:00
case SYS_DVBT :
2014-10-01 06:19:30 +04:00
delivery_system_val = 0x02 ;
2016-02-04 22:01:51 +03:00
reg_bank2_2d_val = 0x23 ;
reg_bank0_d2_val = 0x2a ;
2014-10-01 02:56:22 +04:00
break ;
2014-10-01 00:52:03 +04:00
case SYS_DVBT2 :
2014-10-01 06:19:30 +04:00
delivery_system_val = 0x03 ;
2016-02-04 22:01:51 +03:00
reg_bank2_2d_val = 0x3b ;
reg_bank0_d2_val = 0x29 ;
2014-10-01 00:52:03 +04:00
break ;
case SYS_DVBC_ANNEX_A :
2014-10-01 06:19:30 +04:00
delivery_system_val = 0x04 ;
2016-02-04 22:01:51 +03:00
reg_bank2_2d_val = 0x3b ;
reg_bank0_d2_val = 0x29 ;
2014-10-01 06:19:30 +04:00
break ;
default :
ret = - EINVAL ;
goto err ;
}
2016-02-04 22:01:51 +03:00
switch ( c - > delivery_system ) {
case SYS_DVBT :
case SYS_DVBT2 :
switch ( c - > bandwidth_hz ) {
case 6000000 :
conf_val_ptr = " \xe9 \x55 \x55 \x1c \x29 \x1c \x29 " ;
break ;
case 7000000 :
conf_val_ptr = " \xc8 \x00 \x00 \x17 \x0a \x17 \x0a " ;
break ;
case 8000000 :
conf_val_ptr = " \xaf \x00 \x00 \x11 \xec \x11 \xec " ;
break ;
default :
ret = - EINVAL ;
goto err ;
}
break ;
case SYS_DVBC_ANNEX_A :
conf_val_ptr = " \x10 \xab \x0d \xae \x1d \x9d " ;
break ;
default :
break ;
2014-10-01 00:52:03 +04:00
}
2016-02-04 22:01:51 +03:00
/* Program tuner */
2014-09-30 08:01:47 +04:00
if ( fe - > ops . tuner_ops . set_params ) {
ret = fe - > ops . tuner_ops . set_params ( fe ) ;
if ( ret )
goto err ;
}
if ( fe - > ops . tuner_ops . get_if_frequency ) {
ret = fe - > ops . tuner_ops . get_if_frequency ( fe , & if_frequency ) ;
if ( ret )
goto err ;
2016-02-04 22:01:51 +03:00
dev_dbg ( & client - > dev , " get_if_frequency=%u \n " , if_frequency ) ;
2014-10-01 06:19:30 +04:00
} else {
2016-02-04 22:01:51 +03:00
ret = - EINVAL ;
goto err ;
2014-09-30 08:01:47 +04:00
}
2016-02-04 22:01:51 +03:00
/* Calculate IF registers */
uitmp = DIV_ROUND_CLOSEST_ULL ( ( u64 ) if_frequency * 0x1000000 , dev - > clk ) ;
if_val [ 0 ] = ( uitmp > > 16 ) & 0xff ;
if_val [ 1 ] = ( uitmp > > 8 ) & 0xff ;
if_val [ 2 ] = ( uitmp > > 0 ) & 0xff ;
2014-09-30 08:01:47 +04:00
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x05 , 0x00 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0xfb , 0x13 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0xef , 0x13 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0xf9 , 0x13 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x00 , 0x18 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x01 , 0x01 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x02 , 0x21 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x03 , delivery_system_val ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x0b , 0x00 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-01 00:52:03 +04:00
2014-10-01 06:19:30 +04:00
for ( i = 0 ; i < sizeof ( if_val ) ; i + + ) {
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x10 + i , if_val [ i ] ) ;
2014-10-01 06:19:30 +04:00
if ( ret )
goto err ;
}
2016-02-04 22:01:51 +03:00
switch ( c - > delivery_system ) {
case SYS_DVBT :
case SYS_DVBT2 :
for ( i = 0 ; i < 7 ; i + + ) {
ret = regmap_write ( dev - > regmap [ 2 ] , 0x13 + i ,
conf_val_ptr [ i ] ) ;
if ( ret )
goto err ;
}
break ;
case SYS_DVBC_ANNEX_A :
ret = regmap_bulk_write ( dev - > regmap [ 1 ] , 0x10 , conf_val_ptr , 6 ) ;
2014-10-01 00:52:03 +04:00
if ( ret )
goto err ;
2016-02-04 22:01:51 +03:00
break ;
default :
break ;
2014-10-01 00:52:03 +04:00
}
2016-02-04 22:01:51 +03:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x2d , reg_bank2_2d_val ) ;
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x2e , 0x00 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x56 , 0x0d ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
ret = regmap_bulk_write ( dev - > regmap [ 0 ] , 0x01 ,
" \xba \x13 \x80 \xba \x91 \xdd \xe7 \x28 " , 8 ) ;
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x0a , 0x1a ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x13 , 0x1f ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x19 , 0x03 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x1d , 0xb0 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x2a , 0x72 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x2d , 0x00 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x3c , 0x00 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0x3f , 0xf8 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
ret = regmap_bulk_write ( dev - > regmap [ 0 ] , 0x40 , " \xf4 \x08 " , 2 ) ;
if ( ret )
goto err ;
ret = regmap_write ( dev - > regmap [ 0 ] , 0xd2 , reg_bank0_d2_val ) ;
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0xd4 , 0x55 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 1 ] , 0xbe , 0x08 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0xb2 , 0x37 ) ;
2016-02-04 22:01:51 +03:00
if ( ret )
goto err ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0xd7 , 0x04 ) ;
2014-09-30 08:01:47 +04:00
if ( ret )
goto err ;
2016-02-14 05:41:06 +03:00
/* PLP */
if ( c - > delivery_system = = SYS_DVBT2 ) {
ret = regmap_write ( dev - > regmap [ 2 ] , 0x36 , c - > stream_id ) ;
if ( ret )
goto err ;
}
2016-02-04 22:01:51 +03:00
/* Reset FSM */
ret = regmap_write ( dev - > regmap [ 2 ] , 0xf8 , 0x9f ) ;
if ( ret )
goto err ;
2014-09-30 08:01:47 +04:00
return 0 ;
err :
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-09-30 08:01:47 +04:00
return ret ;
}
2016-11-13 10:25:54 +03:00
static int mn88473_read_status ( struct dvb_frontend * fe , enum fe_status * status )
2016-09-25 01:40:19 +03:00
{
struct i2c_client * client = fe - > demodulator_priv ;
struct mn88473_dev * dev = i2c_get_clientdata ( client ) ;
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2016-11-13 10:25:54 +03:00
int ret , i , stmp ;
unsigned int utmp , utmp1 , utmp2 ;
u8 buf [ 5 ] ;
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
if ( ! dev - > active ) {
ret = - EAGAIN ;
goto err ;
2016-09-25 01:40:19 +03:00
}
2016-11-13 10:25:54 +03:00
/* Lock detection */
switch ( c - > delivery_system ) {
case SYS_DVBT :
ret = regmap_read ( dev - > regmap [ 0 ] , 0x62 , & utmp ) ;
2016-09-25 01:40:19 +03:00
if ( ret )
goto err ;
2016-11-13 10:25:54 +03:00
if ( ! ( utmp & 0xa0 ) ) {
if ( ( utmp & 0x0f ) > = 0x09 )
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK ;
else if ( ( utmp & 0x0f ) > = 0x03 )
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER ;
} else {
* status = 0 ;
}
break ;
case SYS_DVBT2 :
ret = regmap_read ( dev - > regmap [ 2 ] , 0x8b , & utmp ) ;
if ( ret )
goto err ;
if ( ! ( utmp & 0x40 ) ) {
if ( ( utmp & 0x0f ) > = 0x0d )
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK ;
else if ( ( utmp & 0x0f ) > = 0x0a )
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI ;
else if ( ( utmp & 0x0f ) > = 0x07 )
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER ;
} else {
* status = 0 ;
}
break ;
case SYS_DVBC_ANNEX_A :
ret = regmap_read ( dev - > regmap [ 1 ] , 0x85 , & utmp ) ;
2016-09-25 01:40:19 +03:00
if ( ret )
goto err ;
2016-11-13 10:25:54 +03:00
if ( ! ( utmp & 0x40 ) ) {
ret = regmap_read ( dev - > regmap [ 1 ] , 0x89 , & utmp ) ;
if ( ret )
goto err ;
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
if ( utmp & 0x01 )
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK ;
} else {
* status = 0 ;
}
break ;
default :
ret = - EINVAL ;
2016-09-25 01:40:19 +03:00
goto err ;
}
2016-11-13 10:25:54 +03:00
/* Signal strength */
if ( * status & FE_HAS_SIGNAL ) {
for ( i = 0 ; i < 2 ; i + + ) {
ret = regmap_bulk_read ( dev - > regmap [ 2 ] , 0x86 + i ,
& buf [ i ] , 1 ) ;
if ( ret )
goto err ;
}
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
/* AGCRD[15:6] gives us a 10bit value ([5:0] are always 0) */
utmp1 = buf [ 0 ] < < 8 | buf [ 1 ] < < 0 | buf [ 0 ] > > 2 ;
dev_dbg ( & client - > dev , " strength=%u \n " , utmp1 ) ;
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
c - > strength . stat [ 0 ] . scale = FE_SCALE_RELATIVE ;
c - > strength . stat [ 0 ] . uvalue = utmp1 ;
2016-09-25 01:40:19 +03:00
} else {
2016-11-13 10:25:54 +03:00
c - > strength . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
2016-09-25 01:40:19 +03:00
}
/* CNR */
2016-11-13 10:25:54 +03:00
if ( * status & FE_HAS_VITERBI & & c - > delivery_system = = SYS_DVBT ) {
/* DVB-T CNR */
ret = regmap_bulk_read ( dev - > regmap [ 0 ] , 0x8f , buf , 2 ) ;
2016-09-25 01:40:19 +03:00
if ( ret )
goto err ;
2016-11-13 10:25:54 +03:00
utmp = buf [ 0 ] < < 8 | buf [ 1 ] < < 0 ;
if ( utmp ) {
/* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
/* log10(65536) = 80807124, 0.2 = 3355443 */
stmp = div_u64 ( ( ( u64 ) 80807124 - intlog10 ( utmp )
+ 3355443 ) * 10000 , 1 < < 24 ) ;
dev_dbg ( & client - > dev , " cnr=%d value=%u \n " , stmp , utmp ) ;
} else {
stmp = 0 ;
}
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
c - > cnr . stat [ 0 ] . svalue = stmp ;
c - > cnr . stat [ 0 ] . scale = FE_SCALE_DECIBEL ;
} else if ( * status & FE_HAS_VITERBI & &
c - > delivery_system = = SYS_DVBT2 ) {
/* DVB-T2 CNR */
for ( i = 0 ; i < 3 ; i + + ) {
ret = regmap_bulk_read ( dev - > regmap [ 2 ] , 0xb7 + i ,
& buf [ i ] , 1 ) ;
if ( ret )
goto err ;
}
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
utmp = buf [ 1 ] < < 8 | buf [ 2 ] < < 0 ;
utmp1 = ( buf [ 0 ] > > 2 ) & 0x01 ; /* 0=SISO, 1=MISO */
if ( utmp ) {
if ( utmp1 ) {
/* CNR[dB]: 10 * (log10(16384 / value) - 0.6) */
/* log10(16384) = 70706234, 0.6 = 10066330 */
stmp = div_u64 ( ( ( u64 ) 70706234 - intlog10 ( utmp )
- 10066330 ) * 10000 , 1 < < 24 ) ;
dev_dbg ( & client - > dev , " cnr=%d value=%u MISO \n " ,
stmp , utmp ) ;
2016-09-25 01:40:19 +03:00
} else {
2016-11-13 10:25:54 +03:00
/* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
/* log10(65536) = 80807124, 0.2 = 3355443 */
stmp = div_u64 ( ( ( u64 ) 80807124 - intlog10 ( utmp )
+ 3355443 ) * 10000 , 1 < < 24 ) ;
dev_dbg ( & client - > dev , " cnr=%d value=%u SISO \n " ,
stmp , utmp ) ;
2016-09-25 01:40:19 +03:00
}
2016-11-13 10:25:54 +03:00
} else {
stmp = 0 ;
}
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
c - > cnr . stat [ 0 ] . svalue = stmp ;
2016-09-25 01:40:19 +03:00
c - > cnr . stat [ 0 ] . scale = FE_SCALE_DECIBEL ;
2016-11-13 10:25:54 +03:00
} else if ( * status & FE_HAS_VITERBI & &
c - > delivery_system = = SYS_DVBC_ANNEX_A ) {
/* DVB-C CNR */
ret = regmap_bulk_read ( dev - > regmap [ 1 ] , 0xa1 , buf , 4 ) ;
if ( ret )
goto err ;
utmp1 = buf [ 0 ] < < 8 | buf [ 1 ] < < 0 ; /* signal */
utmp2 = buf [ 2 ] < < 8 | buf [ 3 ] < < 0 ; /* noise */
if ( utmp1 & & utmp2 ) {
/* CNR[dB]: 10 * log10(8 * (signal / noise)) */
/* log10(8) = 15151336 */
stmp = div_u64 ( ( ( u64 ) 15151336 + intlog10 ( utmp1 )
- intlog10 ( utmp2 ) ) * 10000 , 1 < < 24 ) ;
dev_dbg ( & client - > dev , " cnr=%d signal=%u noise=%u \n " ,
stmp , utmp1 , utmp2 ) ;
} else {
stmp = 0 ;
}
2016-09-25 01:40:19 +03:00
2016-11-13 10:25:54 +03:00
c - > cnr . stat [ 0 ] . svalue = stmp ;
2016-09-25 01:40:19 +03:00
c - > cnr . stat [ 0 ] . scale = FE_SCALE_DECIBEL ;
} else {
c - > cnr . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
}
/* BER */
2016-11-13 10:25:54 +03:00
if ( * status & FE_HAS_LOCK & & ( c - > delivery_system = = SYS_DVBT | |
c - > delivery_system = = SYS_DVBC_ANNEX_A ) ) {
/* DVB-T & DVB-C BER */
ret = regmap_bulk_read ( dev - > regmap [ 0 ] , 0x92 , buf , 5 ) ;
2015-03-21 13:18:07 +03:00
if ( ret )
goto err ;
2016-02-04 22:01:51 +03:00
2016-11-13 10:25:54 +03:00
utmp1 = buf [ 0 ] < < 16 | buf [ 1 ] < < 8 | buf [ 2 ] < < 0 ;
utmp2 = buf [ 3 ] < < 8 | buf [ 4 ] < < 0 ;
utmp2 = utmp2 * 8 * 204 ;
dev_dbg ( & client - > dev , " post_bit_error=%u post_bit_count=%u \n " ,
utmp1 , utmp2 ) ;
2016-02-04 22:01:51 +03:00
2016-11-13 10:25:54 +03:00
c - > post_bit_error . stat [ 0 ] . scale = FE_SCALE_COUNTER ;
c - > post_bit_error . stat [ 0 ] . uvalue + = utmp1 ;
c - > post_bit_count . stat [ 0 ] . scale = FE_SCALE_COUNTER ;
c - > post_bit_count . stat [ 0 ] . uvalue + = utmp2 ;
2016-09-25 01:40:19 +03:00
} else {
2016-11-13 10:25:54 +03:00
c - > post_bit_error . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
c - > post_bit_count . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
2016-09-25 01:40:19 +03:00
}
/* PER */
if ( * status & FE_HAS_LOCK ) {
2016-11-13 10:25:54 +03:00
ret = regmap_bulk_read ( dev - > regmap [ 0 ] , 0xdd , buf , 4 ) ;
2016-09-25 01:40:19 +03:00
if ( ret )
goto err ;
2016-11-13 10:25:54 +03:00
utmp1 = buf [ 0 ] < < 8 | buf [ 1 ] < < 0 ;
utmp2 = buf [ 2 ] < < 8 | buf [ 3 ] < < 0 ;
dev_dbg ( & client - > dev , " block_error=%u block_count=%u \n " ,
utmp1 , utmp2 ) ;
2016-09-25 01:40:19 +03:00
c - > block_error . stat [ 0 ] . scale = FE_SCALE_COUNTER ;
2016-11-13 10:25:54 +03:00
c - > block_error . stat [ 0 ] . uvalue + = utmp1 ;
2016-09-25 01:40:19 +03:00
c - > block_count . stat [ 0 ] . scale = FE_SCALE_COUNTER ;
2016-11-13 10:25:54 +03:00
c - > block_count . stat [ 0 ] . uvalue + = utmp2 ;
2016-09-25 01:40:19 +03:00
} else {
2016-11-13 10:25:54 +03:00
c - > block_error . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
c - > block_count . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
2015-03-21 13:18:07 +03:00
}
2014-09-30 08:01:47 +04:00
return 0 ;
err :
2016-11-13 10:25:54 +03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-09-30 08:01:47 +04:00
return ret ;
}
static int mn88473_init ( struct dvb_frontend * fe )
{
2014-10-03 05:41:26 +04:00
struct i2c_client * client = fe - > demodulator_priv ;
struct mn88473_dev * dev = i2c_get_clientdata ( client ) ;
2016-09-25 01:40:19 +03:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2016-02-04 22:01:51 +03:00
int ret , len , remain ;
unsigned int uitmp ;
const struct firmware * fw ;
const char * name = MN88473_FIRMWARE ;
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-09-30 08:01:47 +04:00
2016-02-04 22:01:51 +03:00
/* Check if firmware is already running */
ret = regmap_read ( dev - > regmap [ 0 ] , 0xf5 , & uitmp ) ;
2015-03-16 01:57:53 +03:00
if ( ret )
goto err ;
2016-02-04 22:01:51 +03:00
if ( ! ( uitmp & 0x01 ) )
goto warm ;
2014-09-30 08:01:47 +04:00
2016-02-04 22:01:51 +03:00
/* Request the firmware, this will block and timeout */
ret = request_firmware ( & fw , name , & client - > dev ) ;
2014-09-30 08:01:47 +04:00
if ( ret ) {
2016-05-17 17:38:41 +03:00
dev_err ( & client - > dev , " firmware file '%s' not found \n " , name ) ;
2016-02-04 22:01:51 +03:00
goto err ;
2014-09-30 08:01:47 +04:00
}
2016-02-04 22:01:51 +03:00
dev_info ( & client - > dev , " downloading firmware from file '%s' \n " , name ) ;
2014-09-30 08:01:47 +04:00
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0xf5 , 0x03 ) ;
2014-09-30 08:01:47 +04:00
if ( ret )
2016-02-04 22:01:51 +03:00
goto err_release_firmware ;
2014-09-30 08:01:47 +04:00
2016-02-04 22:01:51 +03:00
for ( remain = fw - > size ; remain > 0 ; remain - = ( dev - > i2c_wr_max - 1 ) ) {
len = min ( dev - > i2c_wr_max - 1 , remain ) ;
2014-10-03 07:28:18 +04:00
ret = regmap_bulk_write ( dev - > regmap [ 0 ] , 0xf6 ,
2016-02-04 22:01:51 +03:00
& fw - > data [ fw - > size - remain ] , len ) ;
2014-09-30 08:01:47 +04:00
if ( ret ) {
2016-02-04 22:01:51 +03:00
dev_err ( & client - > dev , " firmware download failed %d \n " ,
2015-02-19 05:05:40 +03:00
ret ) ;
2016-02-04 22:01:51 +03:00
goto err_release_firmware ;
2014-09-30 08:01:47 +04:00
}
}
2016-02-04 22:01:51 +03:00
release_firmware ( fw ) ;
/* Parity check of firmware */
ret = regmap_read ( dev - > regmap [ 0 ] , 0xf8 , & uitmp ) ;
if ( ret )
2015-03-16 01:57:52 +03:00
goto err ;
2016-02-04 22:01:51 +03:00
if ( uitmp & 0x10 ) {
dev_err ( & client - > dev , " firmware parity check failed \n " ) ;
ret = - EINVAL ;
2015-03-16 01:57:52 +03:00
goto err ;
}
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 0 ] , 0xf5 , 0x00 ) ;
2014-09-30 08:01:47 +04:00
if ( ret )
goto err ;
2016-02-04 22:01:51 +03:00
warm :
/* TS config */
ret = regmap_write ( dev - > regmap [ 2 ] , 0x09 , 0x08 ) ;
if ( ret )
goto err ;
ret = regmap_write ( dev - > regmap [ 2 ] , 0x08 , 0x1d ) ;
if ( ret )
goto err ;
2014-09-30 08:01:47 +04:00
2016-02-04 22:01:51 +03:00
dev - > active = true ;
2014-09-30 08:01:47 +04:00
2016-09-25 01:40:19 +03:00
/* init stats here to indicate which stats are supported */
c - > strength . len = 1 ;
c - > strength . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
c - > cnr . len = 1 ;
c - > cnr . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
c - > post_bit_error . len = 1 ;
c - > post_bit_error . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
c - > post_bit_count . len = 1 ;
c - > post_bit_count . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
c - > block_error . len = 1 ;
c - > block_error . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
c - > block_count . len = 1 ;
c - > block_count . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
2014-09-30 08:01:47 +04:00
return 0 ;
2016-02-04 22:01:51 +03:00
err_release_firmware :
2014-12-02 00:55:29 +03:00
release_firmware ( fw ) ;
2016-02-04 22:01:51 +03:00
err :
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-09-30 08:01:47 +04:00
return ret ;
}
static int mn88473_sleep ( struct dvb_frontend * fe )
{
2014-10-03 05:41:26 +04:00
struct i2c_client * client = fe - > demodulator_priv ;
struct mn88473_dev * dev = i2c_get_clientdata ( client ) ;
2014-09-30 08:01:47 +04:00
int ret ;
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-09-30 08:01:47 +04:00
2016-02-04 22:01:51 +03:00
dev - > active = false ;
2014-10-03 07:28:18 +04:00
ret = regmap_write ( dev - > regmap [ 2 ] , 0x05 , 0x3e ) ;
2014-09-30 08:01:47 +04:00
if ( ret )
goto err ;
return 0 ;
err :
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-09-30 08:01:47 +04:00
return ret ;
}
2016-02-04 22:01:51 +03:00
static const struct dvb_frontend_ops mn88473_ops = {
. delsys = { SYS_DVBT , SYS_DVBT2 , SYS_DVBC_ANNEX_A } ,
2014-10-03 05:41:26 +04:00
. info = {
. name = " Panasonic MN88473 " ,
2015-03-17 00:56:21 +03:00
. symbol_rate_min = 1000000 ,
. symbol_rate_max = 7200000 ,
2014-10-03 05:41:26 +04:00
. caps = FE_CAN_FEC_1_2 |
FE_CAN_FEC_2_3 |
FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 |
FE_CAN_FEC_7_8 |
FE_CAN_FEC_AUTO |
FE_CAN_QPSK |
FE_CAN_QAM_16 |
FE_CAN_QAM_32 |
FE_CAN_QAM_64 |
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
FE_CAN_QAM_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO |
FE_CAN_MUTE_TS |
2016-02-14 05:41:06 +03:00
FE_CAN_2G_MODULATION |
FE_CAN_MULTISTREAM
2014-10-03 05:41:26 +04:00
} ,
. get_tune_settings = mn88473_get_tune_settings ,
. init = mn88473_init ,
. sleep = mn88473_sleep ,
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
. set_frontend = mn88473_set_frontend ,
. read_status = mn88473_read_status ,
} ;
static int mn88473_probe ( struct i2c_client * client ,
2015-02-19 05:05:40 +03:00
const struct i2c_device_id * id )
2014-09-30 08:01:47 +04:00
{
2014-10-03 05:41:26 +04:00
struct mn88473_config * config = client - > dev . platform_data ;
2014-09-30 08:01:47 +04:00
struct mn88473_dev * dev ;
2014-10-03 05:41:26 +04:00
int ret ;
2016-02-04 22:01:51 +03:00
unsigned int uitmp ;
2014-10-03 07:28:18 +04:00
static const struct regmap_config regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-09-30 08:01:47 +04:00
2016-02-04 22:01:51 +03:00
/* Caller really need to provide pointer for frontend we create */
2014-10-03 05:41:26 +04:00
if ( config - > fe = = NULL ) {
dev_err ( & client - > dev , " frontend pointer not defined \n " ) ;
ret = - EINVAL ;
goto err ;
}
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( dev = = NULL ) {
2014-09-30 08:01:47 +04:00
ret = - ENOMEM ;
goto err ;
}
2016-02-04 22:01:51 +03:00
if ( config - > i2c_wr_max )
dev - > i2c_wr_max = config - > i2c_wr_max ;
2015-01-13 02:23:25 +03:00
else
2016-02-04 22:01:51 +03:00
dev - > i2c_wr_max = ~ 0 ;
if ( config - > xtal )
dev - > clk = config - > xtal ;
else
dev - > clk = 25000000 ;
2014-10-03 07:28:18 +04:00
dev - > client [ 0 ] = client ;
dev - > regmap [ 0 ] = regmap_init_i2c ( dev - > client [ 0 ] , & regmap_config ) ;
if ( IS_ERR ( dev - > regmap [ 0 ] ) ) {
ret = PTR_ERR ( dev - > regmap [ 0 ] ) ;
goto err_kfree ;
}
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
/*
2016-02-04 22:01:51 +03:00
* Chip has three I2C addresses for different register banks . Used
2014-10-03 05:41:26 +04:00
* addresses are 0x18 , 0x1a and 0x1c . We register two dummy clients ,
2016-02-04 22:01:51 +03:00
* 0x1a and 0x1c , in order to get own I2C client for each register bank .
*
* Also , register bank 2 do not support sequential I / O . Only single
* register write or read is allowed to that bank .
2014-10-03 05:41:26 +04:00
*/
dev - > client [ 1 ] = i2c_new_dummy ( client - > adapter , 0x1a ) ;
if ( dev - > client [ 1 ] = = NULL ) {
ret = - ENODEV ;
dev_err ( & client - > dev , " I2C registration failed \n " ) ;
if ( ret )
2014-10-03 07:28:18 +04:00
goto err_regmap_0_regmap_exit ;
}
dev - > regmap [ 1 ] = regmap_init_i2c ( dev - > client [ 1 ] , & regmap_config ) ;
if ( IS_ERR ( dev - > regmap [ 1 ] ) ) {
ret = PTR_ERR ( dev - > regmap [ 1 ] ) ;
goto err_client_1_i2c_unregister_device ;
2014-10-03 05:41:26 +04:00
}
i2c_set_clientdata ( dev - > client [ 1 ] , dev ) ;
dev - > client [ 2 ] = i2c_new_dummy ( client - > adapter , 0x1c ) ;
if ( dev - > client [ 2 ] = = NULL ) {
ret = - ENODEV ;
dev_err ( & client - > dev , " 2nd I2C registration failed \n " ) ;
if ( ret )
2014-10-03 07:28:18 +04:00
goto err_regmap_1_regmap_exit ;
}
dev - > regmap [ 2 ] = regmap_init_i2c ( dev - > client [ 2 ] , & regmap_config ) ;
if ( IS_ERR ( dev - > regmap [ 2 ] ) ) {
ret = PTR_ERR ( dev - > regmap [ 2 ] ) ;
goto err_client_2_i2c_unregister_device ;
2014-10-03 05:41:26 +04:00
}
i2c_set_clientdata ( dev - > client [ 2 ] , dev ) ;
2014-09-30 08:01:47 +04:00
2016-12-01 00:36:14 +03:00
/* Check demod answers with correct chip id */
ret = regmap_read ( dev - > regmap [ 2 ] , 0xff , & uitmp ) ;
if ( ret )
goto err_regmap_2_regmap_exit ;
dev_dbg ( & client - > dev , " chip id=%02x \n " , uitmp ) ;
if ( uitmp ! = 0x03 ) {
ret = - ENODEV ;
goto err_regmap_2_regmap_exit ;
}
2016-02-04 22:01:51 +03:00
/* Sleep because chip is active by default */
ret = regmap_write ( dev - > regmap [ 2 ] , 0x05 , 0x3e ) ;
if ( ret )
2016-05-14 08:29:21 +03:00
goto err_regmap_2_regmap_exit ;
2016-02-04 22:01:51 +03:00
/* Create dvb frontend */
memcpy ( & dev - > frontend . ops , & mn88473_ops , sizeof ( dev - > frontend . ops ) ) ;
dev - > frontend . demodulator_priv = client ;
* config - > fe = & dev - > frontend ;
2014-10-03 05:41:26 +04:00
i2c_set_clientdata ( client , dev ) ;
2014-09-30 08:01:47 +04:00
2016-02-04 22:01:51 +03:00
dev_info ( & client - > dev , " Panasonic MN88473 successfully identified \n " ) ;
2014-10-03 05:41:26 +04:00
return 0 ;
2016-05-14 08:29:21 +03:00
err_regmap_2_regmap_exit :
regmap_exit ( dev - > regmap [ 2 ] ) ;
2014-10-03 07:28:18 +04:00
err_client_2_i2c_unregister_device :
i2c_unregister_device ( dev - > client [ 2 ] ) ;
err_regmap_1_regmap_exit :
regmap_exit ( dev - > regmap [ 1 ] ) ;
2014-10-03 05:41:26 +04:00
err_client_1_i2c_unregister_device :
i2c_unregister_device ( dev - > client [ 1 ] ) ;
2014-10-03 07:28:18 +04:00
err_regmap_0_regmap_exit :
regmap_exit ( dev - > regmap [ 0 ] ) ;
2014-10-03 05:41:26 +04:00
err_kfree :
2014-09-30 08:01:47 +04:00
kfree ( dev ) ;
2014-10-03 05:41:26 +04:00
err :
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
return ret ;
2014-09-30 08:01:47 +04:00
}
2014-10-03 05:41:26 +04:00
static int mn88473_remove ( struct i2c_client * client )
{
struct mn88473_dev * dev = i2c_get_clientdata ( client ) ;
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-09-30 08:01:47 +04:00
2014-10-03 07:28:18 +04:00
regmap_exit ( dev - > regmap [ 2 ] ) ;
2014-10-03 05:41:26 +04:00
i2c_unregister_device ( dev - > client [ 2 ] ) ;
2014-10-03 07:28:18 +04:00
regmap_exit ( dev - > regmap [ 1 ] ) ;
2014-10-03 05:41:26 +04:00
i2c_unregister_device ( dev - > client [ 1 ] ) ;
2014-09-30 08:01:47 +04:00
2014-10-03 07:28:18 +04:00
regmap_exit ( dev - > regmap [ 0 ] ) ;
2014-10-03 05:41:26 +04:00
kfree ( dev ) ;
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
return 0 ;
}
2014-09-30 08:01:47 +04:00
2014-10-03 05:41:26 +04:00
static const struct i2c_device_id mn88473_id_table [ ] = {
{ " mn88473 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , mn88473_id_table ) ;
static struct i2c_driver mn88473_driver = {
. driver = {
2016-02-04 22:01:51 +03:00
. name = " mn88473 " ,
. suppress_bind_attrs = true ,
2014-10-03 05:41:26 +04:00
} ,
. probe = mn88473_probe ,
. remove = mn88473_remove ,
. id_table = mn88473_id_table ,
2014-09-30 08:01:47 +04:00
} ;
2014-10-03 05:41:26 +04:00
module_i2c_driver ( mn88473_driver ) ;
2014-09-30 08:01:47 +04:00
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_DESCRIPTION ( " Panasonic MN88473 DVB-T/T2/C demodulator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_FIRMWARE ( MN88473_FIRMWARE ) ;