2011-06-01 23:57:11 +04:00
/* SF16-FMR2 radio driver for Linux
* Copyright ( c ) 2011 Ondrej Zary
2005-04-17 02:20:36 +04:00
*
2011-06-01 23:57:11 +04: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-17 02:20:36 +04:00
*/
2011-06-01 23:57:11 +04:00
# include <linux/delay.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h> /* Modules */
# include <linux/init.h> /* Initdata */
2005-09-13 12:25:15 +04:00
# include <linux/ioport.h> /* request_region */
2009-03-06 19:53:26 +03:00
# include <linux/io.h> /* outb, outb_p */
2011-06-01 23:57:11 +04:00
# include <sound/tea575x-tuner.h>
2005-04-17 02:20:36 +04:00
2011-06-01 23:57:11 +04:00
MODULE_AUTHOR ( " Ondrej Zary " ) ;
MODULE_DESCRIPTION ( " MediaForte SF16-FMR2 FM radio card driver " ) ;
2009-03-06 19:53:26 +03:00
MODULE_LICENSE ( " GPL " ) ;
2011-06-01 23:57:11 +04:00
struct fmr2 {
2009-03-06 19:53:26 +03:00
int io ;
2011-06-01 23:57:11 +04:00
struct snd_tea575x tea ;
struct v4l2_ctrl * volume ;
struct v4l2_ctrl * balance ;
2005-04-17 02:20:36 +04:00
} ;
2011-06-01 23:57:11 +04:00
/* the port is hardwired so no need to support multiple cards */
# define FMR2_PORT 0x384
2009-03-06 19:53:26 +03:00
static struct fmr2 fmr2_card ;
2005-04-17 02:20:36 +04:00
2011-06-01 23:57:11 +04: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-17 02:20:36 +04:00
2011-06-01 23:57:11 +04:00
static void fmr2_tea575x_set_pins ( struct snd_tea575x * tea , u8 pins )
2005-04-17 02:20:36 +04:00
{
2011-06-01 23:57:11 +04:00
struct fmr2 * fmr2 = tea - > private_data ;
u8 bits = 0 ;
2005-04-17 02:20:36 +04:00
2011-06-01 23:57:11 +04: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-17 02:20:36 +04:00
2011-06-01 23:57:11 +04:00
outb ( bits , fmr2 - > io ) ;
2005-04-17 02:20:36 +04:00
}
2011-06-01 23:57:11 +04:00
static u8 fmr2_tea575x_get_pins ( struct snd_tea575x * tea )
2005-04-17 02:20:36 +04:00
{
2011-06-01 23:57:11 +04:00
struct fmr2 * fmr2 = tea - > private_data ;
u8 bits = inb ( fmr2 - > io ) ;
2007-04-24 00:51:37 +04:00
2011-06-01 23:57:11 +04:00
return ( bits & STR_DATA ) ? TEA575X_DATA : 0 |
( bits & STR_MOST ) ? TEA575X_MOST : 0 ;
2007-04-24 00:51:37 +04:00
}
2006-08-08 16:10:02 +04:00
2011-06-01 23:57:11 +04:00
static void fmr2_tea575x_set_direction ( struct snd_tea575x * tea , bool output )
2007-04-24 00:51:37 +04:00
{
}
2006-08-08 16:10:02 +04:00
2011-06-01 23:57:11 +04: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 16:10:02 +04:00
2011-06-01 23:57:11 +04: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-24 00:51:37 +04:00
}
2006-08-08 16:10:02 +04:00
2011-06-01 23:57:11 +04:00
/* latch register data */
udelay ( 5 ) ;
tc9154a_set_pins ( fmr2 , PT_ST ) ;
udelay ( 5 ) ;
tc9154a_set_pins ( fmr2 , 0 ) ;
2007-04-24 00:51:37 +04:00
}
2011-06-01 23:57:11 +04:00
static int fmr2_s_ctrl ( struct v4l2_ctrl * ctrl )
2007-04-24 00:51:37 +04:00
{
2011-06-01 23:57:11 +04: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-24 00:51:37 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_VOLUME :
2011-06-01 23:57:11 +04:00
volume = ctrl - > val ;
balance = fmr2 - > balance - > cur . val ;
2007-04-24 00:51:37 +04:00
break ;
2011-06-01 23:57:11 +04:00
case V4L2_CID_AUDIO_BALANCE :
balance = ctrl - > val ;
volume = fmr2 - > volume - > cur . val ;
2007-04-24 00:51:37 +04:00
break ;
default :
return - EINVAL ;
}
2011-06-01 23:57:11 +04:00
left = right = volume ;
if ( balance < 0 )
right = max ( 0 , right + balance ) ;
if ( balance > 0 )
left = max ( 0 , left - balance ) ;
2005-04-17 02:20:36 +04:00
2011-06-01 23:57:11 +04:00
tc9154a_set_attenuation ( fmr2 , abs ( left - 68 ) , TC9154A_CHANNEL_LEFT ) ;
tc9154a_set_attenuation ( fmr2 , abs ( right - 68 ) , TC9154A_CHANNEL_RIGHT ) ;
2007-04-24 00:51:37 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2011-06-01 23:57:11 +04: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 19:53:26 +03:00
{
2011-06-01 23:57:11 +04:00
struct fmr2 * fmr2 = tea - > private_data ;
2005-04-17 02:20:36 +04:00
2011-06-01 23:57:11 +04:00
if ( inb ( fmr2 - > io ) & FMR2_HASVOL ) {
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 ) {
printk ( KERN_ERR " radio-sf16fmr2: can't initialize contrls \n " ) ;
return tea - > ctrl_handler . error ;
}
}
2005-04-17 02:20:36 +04:00
2011-06-01 23:57:11 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int __init fmr2_init ( void )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = & fmr2_card ;
2011-06-01 23:57:11 +04:00
fmr2 - > io = FMR2_PORT ;
2009-03-06 19:53:26 +03:00
2011-06-01 23:57:11 +04:00
if ( ! request_region ( fmr2 - > io , 2 , " SF16-FMR2 " ) ) {
printk ( KERN_ERR " radio-sf16fmr2: I/O port 0x%x already in use \n " , fmr2 - > io ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
2011-06-01 23:57:11 +04:00
fmr2 - > tea . private_data = fmr2 ;
fmr2 - > tea . ops = & fmr2_tea_ops ;
fmr2 - > tea . ext_init = fmr2_tea_ext_init ;
strlcpy ( fmr2 - > tea . card , " SF16-FMR2 " , sizeof ( fmr2 - > tea . card ) ) ;
strcpy ( fmr2 - > tea . bus_info , " ISA " ) ;
2005-04-17 02:20:36 +04:00
2011-06-01 23:57:11 +04:00
if ( snd_tea575x_init ( & fmr2 - > tea ) ) {
printk ( KERN_ERR " radio-sf16fmr2: Unable to detect TEA575x tuner \n " ) ;
2009-03-06 19:53:26 +03:00
release_region ( fmr2 - > io , 2 ) ;
2011-06-01 23:57:11 +04:00
return - ENODEV ;
2009-03-06 19:53:26 +03:00
}
2008-04-22 21:46:03 +04:00
2011-06-01 23:57:11 +04:00
printk ( KERN_INFO " radio-sf16fmr2: SF16-FMR2 radio card at 0x%x. \n " , fmr2 - > io ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-03-06 19:53:26 +03:00
static void __exit fmr2_exit ( void )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = & fmr2_card ;
2011-06-01 23:57:11 +04:00
snd_tea575x_exit ( & fmr2 - > tea ) ;
2009-03-06 19:53:26 +03:00
release_region ( fmr2 - > io , 2 ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( fmr2_init ) ;
2009-03-06 19:53:26 +03:00
module_exit ( fmr2_exit ) ;