2011-01-11 06:37:53 -03:00
/*
* FM Driver for Connectivity chip of Texas Instruments .
* This sub - module of FM driver implements FM TX functionality .
*
* Copyright ( C ) 2011 Texas Instruments
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/delay.h>
# include "fmdrv.h"
# include "fmdrv_common.h"
# include "fmdrv_tx.h"
2011-12-02 06:01:13 -03:00
int fm_tx_set_stereo_mono ( struct fmdev * fmdev , u16 mode )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
if ( fmdev - > tx_data . aud_mode = = mode )
return 0 ;
fmdbg ( " stereo mode: %d \n " , mode ) ;
/* Set Stereo/Mono mode */
payload = ( 1 - mode ) ;
ret = fmc_send_cmd ( fmdev , MONO_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
fmdev - > tx_data . aud_mode = mode ;
return ret ;
}
2011-12-02 06:01:13 -03:00
static int set_rds_text ( struct fmdev * fmdev , u8 * rds_text )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
ret = fmc_send_cmd ( fmdev , RDS_DATA_SET , REG_WR , rds_text ,
strlen ( rds_text ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* Scroll mode */
payload = ( u16 ) 0x1 ;
ret = fmc_send_cmd ( fmdev , DISPLAY_MODE , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2011-12-02 06:01:13 -03:00
static int set_rds_data_mode ( struct fmdev * fmdev , u8 mode )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
/* Setting unique PI TODO: how unique? */
payload = ( u16 ) 0xcafe ;
ret = fmc_send_cmd ( fmdev , PI_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* Set decoder id */
payload = ( u16 ) 0xa ;
ret = fmc_send_cmd ( fmdev , DI_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* TODO: RDS_MODE_GET? */
return 0 ;
}
2011-12-02 06:01:13 -03:00
static int set_rds_len ( struct fmdev * fmdev , u8 type , u16 len )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
len | = type < < 8 ;
payload = len ;
ret = fmc_send_cmd ( fmdev , RDS_CONFIG_DATA_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* TODO: LENGTH_GET? */
return 0 ;
}
2011-12-02 06:01:13 -03:00
int fm_tx_set_rds_mode ( struct fmdev * fmdev , u8 rds_en_dis )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
u8 rds_text [ ] = " Zoom2 \n " ;
fmdbg ( " rds_en_dis:%d(E:%d, D:%d) \n " , rds_en_dis ,
FM_RDS_ENABLE , FM_RDS_DISABLE ) ;
if ( rds_en_dis = = FM_RDS_ENABLE ) {
/* Set RDS length */
set_rds_len ( fmdev , 0 , strlen ( rds_text ) ) ;
/* Set RDS text */
set_rds_text ( fmdev , rds_text ) ;
/* Set RDS mode */
set_rds_data_mode ( fmdev , 0x0 ) ;
}
/* Send command to enable RDS */
if ( rds_en_dis = = FM_RDS_ENABLE )
payload = 0x01 ;
else
payload = 0x00 ;
ret = fmc_send_cmd ( fmdev , RDS_DATA_ENB , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
if ( rds_en_dis = = FM_RDS_ENABLE ) {
/* Set RDS length */
set_rds_len ( fmdev , 0 , strlen ( rds_text ) ) ;
/* Set RDS text */
set_rds_text ( fmdev , rds_text ) ;
}
fmdev - > tx_data . rds . flag = rds_en_dis ;
return 0 ;
}
2011-12-02 06:01:13 -03:00
int fm_tx_set_radio_text ( struct fmdev * fmdev , u8 * rds_text , u8 rds_type )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
if ( fmdev - > curr_fmmode ! = FM_MODE_TX )
return - EPERM ;
fm_tx_set_rds_mode ( fmdev , 0 ) ;
/* Set RDS length */
set_rds_len ( fmdev , rds_type , strlen ( rds_text ) ) ;
/* Set RDS text */
set_rds_text ( fmdev , rds_text ) ;
/* Set RDS mode */
set_rds_data_mode ( fmdev , 0x0 ) ;
payload = 1 ;
ret = fmc_send_cmd ( fmdev , RDS_DATA_ENB , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2011-12-02 06:01:13 -03:00
int fm_tx_set_af ( struct fmdev * fmdev , u32 af )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
if ( fmdev - > curr_fmmode ! = FM_MODE_TX )
return - EPERM ;
fmdbg ( " AF: %d \n " , af ) ;
af = ( af - 87500 ) / 100 ;
payload = ( u16 ) af ;
ret = fmc_send_cmd ( fmdev , TA_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2011-12-02 06:01:13 -03:00
int fm_tx_set_region ( struct fmdev * fmdev , u8 region )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
if ( region ! = FM_BAND_EUROPE_US & & region ! = FM_BAND_JAPAN ) {
fmerr ( " Invalid band \n " ) ;
return - EINVAL ;
}
/* Send command to set the band */
payload = ( u16 ) region ;
ret = fmc_send_cmd ( fmdev , TX_BAND_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2011-12-02 06:01:13 -03:00
int fm_tx_set_mute_mode ( struct fmdev * fmdev , u8 mute_mode_toset )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
fmdbg ( " tx: mute mode %d \n " , mute_mode_toset ) ;
payload = mute_mode_toset ;
ret = fmc_send_cmd ( fmdev , MUTE , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
/* Set TX Audio I/O */
2011-12-02 06:01:13 -03:00
static int set_audio_io ( struct fmdev * fmdev )
2011-01-11 06:37:53 -03:00
{
struct fmtx_data * tx = & fmdev - > tx_data ;
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
/* Set Audio I/O Enable */
payload = tx - > audio_io ;
ret = fmc_send_cmd ( fmdev , AUDIO_IO_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* TODO: is audio set? */
return 0 ;
}
/* Start TX Transmission */
2011-12-02 06:01:13 -03:00
static int enable_xmit ( struct fmdev * fmdev , u8 new_xmit_state )
2011-01-11 06:37:53 -03:00
{
struct fmtx_data * tx = & fmdev - > tx_data ;
unsigned long timeleft ;
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
/* Enable POWER_ENB interrupts */
payload = FM_POW_ENB_EVENT ;
ret = fmc_send_cmd ( fmdev , INT_MASK_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* Set Power Enable */
payload = new_xmit_state ;
ret = fmc_send_cmd ( fmdev , POWER_ENB_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* Wait for Power Enabled */
init_completion ( & fmdev - > maintask_comp ) ;
timeleft = wait_for_completion_timeout ( & fmdev - > maintask_comp ,
FM_DRV_TX_TIMEOUT ) ;
if ( ! timeleft ) {
fmerr ( " Timeout(%d sec),didn't get tune ended interrupt \n " ,
jiffies_to_msecs ( FM_DRV_TX_TIMEOUT ) / 1000 ) ;
return - ETIMEDOUT ;
}
set_bit ( FM_CORE_TX_XMITING , & fmdev - > flag ) ;
tx - > xmit_state = new_xmit_state ;
return 0 ;
}
/* Set TX power level */
2011-12-02 06:01:13 -03:00
int fm_tx_set_pwr_lvl ( struct fmdev * fmdev , u8 new_pwr_lvl )
2011-01-11 06:37:53 -03:00
{
u16 payload ;
struct fmtx_data * tx = & fmdev - > tx_data ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
if ( fmdev - > curr_fmmode ! = FM_MODE_TX )
return - EPERM ;
fmdbg ( " tx: pwr_level_to_set %ld \n " , ( long int ) new_pwr_lvl ) ;
/* If the core isn't ready update global variable */
if ( ! test_bit ( FM_CORE_READY , & fmdev - > flag ) ) {
tx - > pwr_lvl = new_pwr_lvl ;
return 0 ;
}
/* Set power level: Application will specify power level value in
* units of dB / uV , whereas range and step are specific to FM chip .
* For TI ' s WL chips , convert application specified power level value
* to chip specific value by subtracting 122 from it . Refer to TI FM
* data sheet for details .
* */
payload = ( FM_PWR_LVL_HIGH - new_pwr_lvl ) ;
ret = fmc_send_cmd ( fmdev , POWER_LEV_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
/* TODO: is the power level set? */
tx - > pwr_lvl = new_pwr_lvl ;
return 0 ;
}
/*
* Sets FM TX pre - emphasis filter value ( OFF , 50u s , or 75u s )
* Convert V4L2 specified filter values to chip specific filter values .
*/
2011-12-02 06:01:13 -03:00
int fm_tx_set_preemph_filter ( struct fmdev * fmdev , u32 preemphasis )
2011-01-11 06:37:53 -03:00
{
struct fmtx_data * tx = & fmdev - > tx_data ;
u16 payload ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
if ( fmdev - > curr_fmmode ! = FM_MODE_TX )
return - EPERM ;
switch ( preemphasis ) {
case V4L2_PREEMPHASIS_DISABLED :
payload = FM_TX_PREEMPH_OFF ;
break ;
case V4L2_PREEMPHASIS_50_uS :
payload = FM_TX_PREEMPH_50US ;
break ;
case V4L2_PREEMPHASIS_75_uS :
payload = FM_TX_PREEMPH_75US ;
break ;
}
ret = fmc_send_cmd ( fmdev , PREMPH_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
tx - > preemph = payload ;
return ret ;
}
/* Get the TX tuning capacitor value.*/
2011-12-02 06:01:13 -03:00
int fm_tx_get_tune_cap_val ( struct fmdev * fmdev )
2011-01-11 06:37:53 -03:00
{
u16 curr_val ;
2011-12-02 06:01:13 -03:00
u32 resp_len ;
int ret ;
2011-01-11 06:37:53 -03:00
if ( fmdev - > curr_fmmode ! = FM_MODE_TX )
return - EPERM ;
ret = fmc_send_cmd ( fmdev , READ_FMANT_TUNE_VALUE , REG_RD ,
NULL , sizeof ( curr_val ) , & curr_val , & resp_len ) ;
if ( ret < 0 )
return ret ;
curr_val = be16_to_cpu ( curr_val ) ;
return curr_val ;
}
/* Set TX Frequency */
2011-12-02 06:01:13 -03:00
int fm_tx_set_freq ( struct fmdev * fmdev , u32 freq_to_set )
2011-01-11 06:37:53 -03:00
{
struct fmtx_data * tx = & fmdev - > tx_data ;
u16 payload , chanl_index ;
2011-12-02 06:01:13 -03:00
int ret ;
2011-01-11 06:37:53 -03:00
if ( test_bit ( FM_CORE_TX_XMITING , & fmdev - > flag ) ) {
enable_xmit ( fmdev , 0 ) ;
clear_bit ( FM_CORE_TX_XMITING , & fmdev - > flag ) ;
}
/* Enable FR, BL interrupts */
payload = ( FM_FR_EVENT | FM_BL_EVENT ) ;
ret = fmc_send_cmd ( fmdev , INT_MASK_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
tx - > tx_frq = ( unsigned long ) freq_to_set ;
fmdbg ( " tx: freq_to_set %ld \n " , ( long int ) tx - > tx_frq ) ;
chanl_index = freq_to_set / 10 ;
/* Set current tuner channel */
payload = chanl_index ;
ret = fmc_send_cmd ( fmdev , CHANL_SET , REG_WR , & payload ,
sizeof ( payload ) , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
fm_tx_set_pwr_lvl ( fmdev , tx - > pwr_lvl ) ;
fm_tx_set_preemph_filter ( fmdev , tx - > preemph ) ;
tx - > audio_io = 0x01 ; /* I2S */
set_audio_io ( fmdev ) ;
enable_xmit ( fmdev , 0x01 ) ; /* Enable transmission */
tx - > aud_mode = FM_STEREO_MODE ;
tx - > rds . flag = FM_RDS_DISABLE ;
return 0 ;
}