2012-05-17 04:55:01 -03:00
/* SF16-FMR2 and SF16-FMD2 radio driver for Linux
2011-06-01 16:57:11 -03:00
* Copyright ( c ) 2011 Ondrej Zary
2005-04-16 15:20:36 -07:00
*
2011-06-01 16:57:11 -03:00
* Original driver was ( c ) 2000 - 2002 Ziglio Frediano , freddy77 @ angelfire . com
* but almost nothing remained here after conversion to generic TEA575x
* implementation
2005-04-16 15:20:36 -07:00
*/
2011-06-01 16:57:11 -03:00
# include <linux/delay.h>
2005-04-16 15:20:36 -07:00
# include <linux/module.h> /* Modules */
# include <linux/init.h> /* Initdata */
2012-02-27 05:30:13 -03:00
# include <linux/slab.h>
2005-09-13 01:25:15 -07:00
# include <linux/ioport.h> /* request_region */
2009-03-06 13:53:26 -03:00
# include <linux/io.h> /* outb, outb_p */
2012-02-27 05:30:13 -03:00
# include <linux/isa.h>
2012-05-17 04:55:01 -03:00
# include <linux/pnp.h>
2013-07-28 16:01:43 -03:00
# include <media/tea575x.h>
2005-04-16 15:20:36 -07:00
2011-06-01 16:57:11 -03:00
MODULE_AUTHOR ( " Ondrej Zary " ) ;
2012-05-17 04:55:01 -03:00
MODULE_DESCRIPTION ( " MediaForte SF16-FMR2 and SF16-FMD2 FM radio card driver " ) ;
2009-03-06 13:53:26 -03:00
MODULE_LICENSE ( " GPL " ) ;
2012-05-17 04:55:01 -03:00
/* these cards can only use two different ports (0x384 and 0x284) */
# define FMR2_MAX 2
static int radio_nr [ FMR2_MAX ] = { [ 0 . . . ( FMR2_MAX - 1 ) ] = - 1 } ;
module_param_array ( radio_nr , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( radio_nr , " Radio device numbers " ) ;
2012-02-27 05:30:13 -03:00
2011-06-01 16:57:11 -03:00
struct fmr2 {
2009-03-06 13:53:26 -03:00
int io ;
2012-02-27 05:30:13 -03:00
struct v4l2_device v4l2_dev ;
2011-06-01 16:57:11 -03:00
struct snd_tea575x tea ;
struct v4l2_ctrl * volume ;
struct v4l2_ctrl * balance ;
2012-05-17 04:55:01 -03:00
bool is_fmd2 ;
2005-04-16 15:20:36 -07:00
} ;
2012-05-17 04:55:01 -03:00
static int num_fmr2_cards ;
static struct fmr2 * fmr2_cards [ FMR2_MAX ] ;
static bool isa_registered ;
static bool pnp_registered ;
/* the port is hardwired on SF16-FMR2 */
2011-06-01 16:57:11 -03:00
# define FMR2_PORT 0x384
2005-04-16 15:20:36 -07:00
2011-06-01 16:57:11 -03:00
/* TEA575x tuner pins */
# define STR_DATA (1 << 0)
# define STR_CLK (1 << 1)
# define STR_WREN (1 << 2)
# define STR_MOST (1 << 3)
/* PT2254A/TC9154A volume control pins */
# define PT_ST (1 << 4)
# define PT_CK (1 << 5)
# define PT_DATA (1 << 6)
/* volume control presence pin */
# define FMR2_HASVOL (1 << 7)
2005-04-16 15:20:36 -07:00
2011-06-01 16:57:11 -03:00
static void fmr2_tea575x_set_pins ( struct snd_tea575x * tea , u8 pins )
2005-04-16 15:20:36 -07:00
{
2011-06-01 16:57:11 -03:00
struct fmr2 * fmr2 = tea - > private_data ;
u8 bits = 0 ;
2005-04-16 15:20:36 -07:00
2011-06-01 16:57:11 -03:00
bits | = ( pins & TEA575X_DATA ) ? STR_DATA : 0 ;
bits | = ( pins & TEA575X_CLK ) ? STR_CLK : 0 ;
/* WRITE_ENABLE is inverted, DATA must be high during read */
bits | = ( pins & TEA575X_WREN ) ? 0 : STR_WREN | STR_DATA ;
2005-04-16 15:20:36 -07:00
2011-06-01 16:57:11 -03:00
outb ( bits , fmr2 - > io ) ;
2005-04-16 15:20:36 -07:00
}
2011-06-01 16:57:11 -03:00
static u8 fmr2_tea575x_get_pins ( struct snd_tea575x * tea )
2005-04-16 15:20:36 -07:00
{
2011-06-01 16:57:11 -03:00
struct fmr2 * fmr2 = tea - > private_data ;
u8 bits = inb ( fmr2 - > io ) ;
2007-04-23 17:51:37 -03:00
2013-08-22 13:07:17 -03:00
return ( ( bits & STR_DATA ) ? TEA575X_DATA : 0 ) |
( ( bits & STR_MOST ) ? TEA575X_MOST : 0 ) ;
2007-04-23 17:51:37 -03:00
}
2006-08-08 09:10:02 -03:00
2011-06-01 16:57:11 -03:00
static void fmr2_tea575x_set_direction ( struct snd_tea575x * tea , bool output )
2007-04-23 17:51:37 -03:00
{
}
2006-08-08 09:10:02 -03:00
2011-06-01 16:57:11 -03:00
static struct snd_tea575x_ops fmr2_tea_ops = {
. set_pins = fmr2_tea575x_set_pins ,
. get_pins = fmr2_tea575x_get_pins ,
. set_direction = fmr2_tea575x_set_direction ,
} ;
2006-08-08 09:10:02 -03:00
2011-06-01 16:57:11 -03:00
/* TC9154A/PT2254A volume control */
/* 18-bit shift register bit definitions */
# define TC9154A_ATT_MAJ_0DB (1 << 0)
# define TC9154A_ATT_MAJ_10DB (1 << 1)
# define TC9154A_ATT_MAJ_20DB (1 << 2)
# define TC9154A_ATT_MAJ_30DB (1 << 3)
# define TC9154A_ATT_MAJ_40DB (1 << 4)
# define TC9154A_ATT_MAJ_50DB (1 << 5)
# define TC9154A_ATT_MAJ_60DB (1 << 6)
# define TC9154A_ATT_MIN_0DB (1 << 7)
# define TC9154A_ATT_MIN_2DB (1 << 8)
# define TC9154A_ATT_MIN_4DB (1 << 9)
# define TC9154A_ATT_MIN_6DB (1 << 10)
# define TC9154A_ATT_MIN_8DB (1 << 11)
/* bit 12 is ignored */
# define TC9154A_CHANNEL_LEFT (1 << 13)
# define TC9154A_CHANNEL_RIGHT (1 << 14)
/* bits 15, 16, 17 must be 0 */
# define TC9154A_ATT_MAJ(x) (1 << x)
# define TC9154A_ATT_MIN(x) (1 << (7 + x))
static void tc9154a_set_pins ( struct fmr2 * fmr2 , u8 pins )
{
if ( ! fmr2 - > tea . mute )
pins | = STR_WREN ;
outb ( pins , fmr2 - > io ) ;
}
static void tc9154a_set_attenuation ( struct fmr2 * fmr2 , int att , u32 channel )
{
int i ;
u32 reg ;
u8 bit ;
reg = TC9154A_ATT_MAJ ( att / 10 ) | TC9154A_ATT_MIN ( ( att % 10 ) / 2 ) ;
reg | = channel ;
/* write 18-bit shift register, LSB first */
for ( i = 0 ; i < 18 ; i + + ) {
bit = reg & ( 1 < < i ) ? PT_DATA : 0 ;
tc9154a_set_pins ( fmr2 , bit ) ;
udelay ( 5 ) ;
tc9154a_set_pins ( fmr2 , bit | PT_CK ) ;
udelay ( 5 ) ;
tc9154a_set_pins ( fmr2 , bit ) ;
2007-04-23 17:51:37 -03:00
}
2006-08-08 09:10:02 -03:00
2011-06-01 16:57:11 -03:00
/* latch register data */
udelay ( 5 ) ;
tc9154a_set_pins ( fmr2 , PT_ST ) ;
udelay ( 5 ) ;
tc9154a_set_pins ( fmr2 , 0 ) ;
2007-04-23 17:51:37 -03:00
}
2011-06-01 16:57:11 -03:00
static int fmr2_s_ctrl ( struct v4l2_ctrl * ctrl )
2007-04-23 17:51:37 -03:00
{
2011-06-01 16:57:11 -03:00
struct snd_tea575x * tea = container_of ( ctrl - > handler , struct snd_tea575x , ctrl_handler ) ;
struct fmr2 * fmr2 = tea - > private_data ;
int volume , balance , left , right ;
2007-04-23 17:51:37 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_VOLUME :
2011-06-01 16:57:11 -03:00
volume = ctrl - > val ;
balance = fmr2 - > balance - > cur . val ;
2007-04-23 17:51:37 -03:00
break ;
2011-06-01 16:57:11 -03:00
case V4L2_CID_AUDIO_BALANCE :
balance = ctrl - > val ;
volume = fmr2 - > volume - > cur . val ;
2007-04-23 17:51:37 -03:00
break ;
default :
return - EINVAL ;
}
2011-06-01 16:57:11 -03:00
left = right = volume ;
if ( balance < 0 )
right = max ( 0 , right + balance ) ;
if ( balance > 0 )
left = max ( 0 , left - balance ) ;
2005-04-16 15:20:36 -07:00
2011-06-01 16:57:11 -03:00
tc9154a_set_attenuation ( fmr2 , abs ( left - 68 ) , TC9154A_CHANNEL_LEFT ) ;
tc9154a_set_attenuation ( fmr2 , abs ( right - 68 ) , TC9154A_CHANNEL_RIGHT ) ;
2007-04-23 17:51:37 -03:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2011-06-01 16:57:11 -03:00
static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
. s_ctrl = fmr2_s_ctrl ,
} ;
static int fmr2_tea_ext_init ( struct snd_tea575x * tea )
2009-03-06 13:53:26 -03:00
{
2011-06-01 16:57:11 -03:00
struct fmr2 * fmr2 = tea - > private_data ;
2005-04-16 15:20:36 -07:00
2012-05-17 04:55:01 -03:00
/* FMR2 can have volume control, FMD2 can't (uses SB16 mixer) */
if ( ! fmr2 - > is_fmd2 & & inb ( fmr2 - > io ) & FMR2_HASVOL ) {
2011-06-01 16:57:11 -03:00
fmr2 - > volume = v4l2_ctrl_new_std ( & tea - > ctrl_handler , & fmr2_ctrl_ops , V4L2_CID_AUDIO_VOLUME , 0 , 68 , 2 , 56 ) ;
fmr2 - > balance = v4l2_ctrl_new_std ( & tea - > ctrl_handler , & fmr2_ctrl_ops , V4L2_CID_AUDIO_BALANCE , - 68 , 68 , 2 , 0 ) ;
if ( tea - > ctrl_handler . error ) {
2012-02-14 10:06:09 -03:00
printk ( KERN_ERR " radio-sf16fmr2: can't initialize controls \n " ) ;
2011-06-01 16:57:11 -03:00
return tea - > ctrl_handler . error ;
}
}
2005-04-16 15:20:36 -07:00
2011-06-01 16:57:11 -03:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
2012-12-21 13:17:53 -08:00
static struct pnp_device_id fmr2_pnp_ids [ ] = {
2012-05-17 04:55:01 -03:00
{ . id = " MFRad13 " } , /* tuner subdevice of SF16-FMD2 */
{ . id = " " }
} ;
MODULE_DEVICE_TABLE ( pnp , fmr2_pnp_ids ) ;
2012-12-21 13:17:53 -08:00
static int fmr2_probe ( struct fmr2 * fmr2 , struct device * pdev , int io )
2005-04-16 15:20:36 -07:00
{
2012-05-17 04:55:01 -03:00
int err , i ;
char * card_name = fmr2 - > is_fmd2 ? " SF16-FMD2 " : " SF16-FMR2 " ;
2012-02-27 05:30:13 -03:00
2012-05-17 04:55:01 -03:00
/* avoid errors if a card was already registered at given port */
for ( i = 0 ; i < num_fmr2_cards ; i + + )
if ( io = = fmr2_cards [ i ] - > io )
return - EBUSY ;
2009-03-06 13:53:26 -03:00
2012-05-17 04:55:01 -03:00
strlcpy ( fmr2 - > v4l2_dev . name , " radio-sf16fmr2 " ,
sizeof ( fmr2 - > v4l2_dev . name ) ) ,
fmr2 - > io = io ;
2009-03-06 13:53:26 -03:00
2012-02-27 05:30:13 -03:00
if ( ! request_region ( fmr2 - > io , 2 , fmr2 - > v4l2_dev . name ) ) {
2011-06-01 16:57:11 -03:00
printk ( KERN_ERR " radio-sf16fmr2: I/O port 0x%x already in use \n " , fmr2 - > io ) ;
2005-04-16 15:20:36 -07:00
return - EBUSY ;
}
2012-02-27 05:30:13 -03:00
dev_set_drvdata ( pdev , fmr2 ) ;
err = v4l2_device_register ( pdev , & fmr2 - > v4l2_dev ) ;
if ( err < 0 ) {
v4l2_err ( & fmr2 - > v4l2_dev , " Could not register v4l2_device \n " ) ;
release_region ( fmr2 - > io , 2 ) ;
return err ;
}
fmr2 - > tea . v4l2_dev = & fmr2 - > v4l2_dev ;
2011-06-01 16:57:11 -03:00
fmr2 - > tea . private_data = fmr2 ;
2012-05-17 04:55:01 -03:00
fmr2 - > tea . radio_nr = radio_nr [ num_fmr2_cards ] ;
2011-06-01 16:57:11 -03:00
fmr2 - > tea . ops = & fmr2_tea_ops ;
fmr2 - > tea . ext_init = fmr2_tea_ext_init ;
2012-05-17 04:55:01 -03:00
strlcpy ( fmr2 - > tea . card , card_name , sizeof ( fmr2 - > tea . card ) ) ;
snprintf ( fmr2 - > tea . bus_info , sizeof ( fmr2 - > tea . bus_info ) , " %s:%s " ,
fmr2 - > is_fmd2 ? " PnP " : " ISA " , dev_name ( pdev ) ) ;
2005-04-16 15:20:36 -07:00
2012-05-19 07:57:03 -03:00
if ( snd_tea575x_init ( & fmr2 - > tea , THIS_MODULE ) ) {
2011-06-01 16:57:11 -03:00
printk ( KERN_ERR " radio-sf16fmr2: Unable to detect TEA575x tuner \n " ) ;
2009-03-06 13:53:26 -03:00
release_region ( fmr2 - > io , 2 ) ;
2011-06-01 16:57:11 -03:00
return - ENODEV ;
2009-03-06 13:53:26 -03:00
}
2008-04-22 14:46:03 -03:00
2012-05-17 04:55:01 -03:00
printk ( KERN_INFO " radio-sf16fmr2: %s radio card at 0x%x. \n " ,
card_name , fmr2 - > io ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2012-12-21 13:17:53 -08:00
static int fmr2_isa_match ( struct device * pdev , unsigned int ndev )
2012-05-17 04:55:01 -03:00
{
struct fmr2 * fmr2 = kzalloc ( sizeof ( * fmr2 ) , GFP_KERNEL ) ;
if ( ! fmr2 )
return 0 ;
if ( fmr2_probe ( fmr2 , pdev , FMR2_PORT ) ) {
kfree ( fmr2 ) ;
return 0 ;
}
dev_set_drvdata ( pdev , fmr2 ) ;
fmr2_cards [ num_fmr2_cards + + ] = fmr2 ;
return 1 ;
}
2012-12-21 13:17:53 -08:00
static int fmr2_pnp_probe ( struct pnp_dev * pdev , const struct pnp_device_id * id )
2005-04-16 15:20:36 -07:00
{
2012-05-17 04:55:01 -03:00
int ret ;
struct fmr2 * fmr2 = kzalloc ( sizeof ( * fmr2 ) , GFP_KERNEL ) ;
if ( ! fmr2 )
return - ENOMEM ;
2009-03-06 13:53:26 -03:00
2012-05-17 04:55:01 -03:00
fmr2 - > is_fmd2 = true ;
ret = fmr2_probe ( fmr2 , & pdev - > dev , pnp_port_start ( pdev , 0 ) ) ;
if ( ret ) {
kfree ( fmr2 ) ;
return ret ;
}
pnp_set_drvdata ( pdev , fmr2 ) ;
fmr2_cards [ num_fmr2_cards + + ] = fmr2 ;
return 0 ;
}
2012-12-21 13:17:53 -08:00
static void fmr2_remove ( struct fmr2 * fmr2 )
2012-05-17 04:55:01 -03:00
{
2011-06-01 16:57:11 -03:00
snd_tea575x_exit ( & fmr2 - > tea ) ;
2009-03-06 13:53:26 -03:00
release_region ( fmr2 - > io , 2 ) ;
2012-02-27 05:30:13 -03:00
v4l2_device_unregister ( & fmr2 - > v4l2_dev ) ;
kfree ( fmr2 ) ;
2012-05-17 04:55:01 -03:00
}
2012-12-21 13:17:53 -08:00
static int fmr2_isa_remove ( struct device * pdev , unsigned int ndev )
2012-05-17 04:55:01 -03:00
{
fmr2_remove ( dev_get_drvdata ( pdev ) ) ;
2012-02-27 05:30:13 -03:00
return 0 ;
}
2012-12-21 13:17:53 -08:00
static void fmr2_pnp_remove ( struct pnp_dev * pdev )
2012-05-17 04:55:01 -03:00
{
fmr2_remove ( pnp_get_drvdata ( pdev ) ) ;
pnp_set_drvdata ( pdev , NULL ) ;
}
2014-09-24 15:44:08 -03:00
static struct isa_driver fmr2_isa_driver = {
2012-05-17 04:55:01 -03:00
. match = fmr2_isa_match ,
2012-12-21 13:17:53 -08:00
. remove = fmr2_isa_remove ,
2012-02-27 05:30:13 -03:00
. driver = {
. name = " radio-sf16fmr2 " ,
} ,
} ;
2014-09-24 15:44:08 -03:00
static struct pnp_driver fmr2_pnp_driver = {
2012-05-17 04:55:01 -03:00
. name = " radio-sf16fmr2 " ,
. id_table = fmr2_pnp_ids ,
. probe = fmr2_pnp_probe ,
2012-12-21 13:17:53 -08:00
. remove = fmr2_pnp_remove ,
2012-05-17 04:55:01 -03:00
} ;
2012-02-27 05:30:13 -03:00
static int __init fmr2_init ( void )
{
2012-05-17 04:55:01 -03:00
int ret ;
ret = pnp_register_driver ( & fmr2_pnp_driver ) ;
if ( ! ret )
pnp_registered = true ;
ret = isa_register_driver ( & fmr2_isa_driver , 1 ) ;
if ( ! ret )
isa_registered = true ;
return ( pnp_registered | | isa_registered ) ? 0 : ret ;
2012-02-27 05:30:13 -03:00
}
static void __exit fmr2_exit ( void )
{
2012-05-17 04:55:01 -03:00
if ( pnp_registered )
pnp_unregister_driver ( & fmr2_pnp_driver ) ;
if ( isa_registered )
isa_unregister_driver ( & fmr2_isa_driver ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( fmr2_init ) ;
2009-03-06 13:53:26 -03:00
module_exit ( fmr2_exit ) ;