2005-04-16 15:20:36 -07:00
/* Typhoon Radio Card driver for radio support
* ( c ) 1999 Dr . Henrik Seidel < Henrik . Seidel @ gmx . de >
*
* Notes on the hardware
*
* This card has two output sockets , one for speakers and one for line .
* The speaker output has volume control , but only in four discrete
* steps . The line output has neither volume control nor mute .
*
* The card has auto - stereo according to its manual , although it all
* sounds mono to me ( even with the Win / DOS drivers ) . Maybe it ' s my
* antenna - I really don ' t know for sure .
*
* Frequency control is done digitally .
*
* Volume control is done digitally , but there are only four different
* possible values . So you should better always turn the volume up and
* use line control . I got the best results by connecting line output
* to the sound card microphone input . For such a configuration the
* volume control has no effect , since volume control only influences
* the speaker output .
*
* There is no explicit mute / unmute . So I set the radio frequency to a
* value where I do expect just noise and turn the speaker volume down .
* The frequency change is necessary since the card never seems to be
* completely silent .
2006-08-08 09:10:04 -03:00
*
* Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
2005-04-16 15:20:36 -07:00
*/
# include <linux/module.h> /* Modules */
# include <linux/init.h> /* Initdata */
2005-09-13 01:25:15 -07:00
# include <linux/ioport.h> /* request_region */
2006-08-08 09:10:04 -03:00
# include <linux/videodev2.h> /* kernel radio structs */
2009-03-06 13:54:52 -03:00
# include <linux/io.h> /* outb, outb_p */
2012-02-29 05:50:27 -03:00
# include <linux/slab.h>
2009-03-06 13:54:52 -03:00
# include <media/v4l2-device.h>
2008-07-20 08:12:02 -03:00
# include <media/v4l2-ioctl.h>
2012-01-16 05:17:32 -03:00
# include "radio-isa.h"
2005-04-16 15:20:36 -07:00
2011-06-25 10:15:42 -03:00
# define DRIVER_VERSION "0.1.2"
2009-03-06 13:54:52 -03:00
MODULE_AUTHOR ( " Dr. Henrik Seidel " ) ;
MODULE_DESCRIPTION ( " A driver for the Typhoon radio card (a.k.a. EcoRadio). " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-01-16 05:17:32 -03:00
MODULE_VERSION ( " 0.1.99 " ) ;
2005-04-16 15:20:36 -07:00
# ifndef CONFIG_RADIO_TYPHOON_PORT
# define CONFIG_RADIO_TYPHOON_PORT -1
# endif
# ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
2012-01-16 05:17:32 -03:00
# define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000
2005-04-16 15:20:36 -07:00
# endif
2012-01-16 05:17:32 -03:00
# define TYPHOON_MAX 2
2009-03-06 13:54:52 -03:00
2012-01-16 05:17:32 -03:00
static int io [ TYPHOON_MAX ] = { [ 0 ] = CONFIG_RADIO_TYPHOON_PORT ,
[ 1 . . . ( TYPHOON_MAX - 1 ) ] = - 1 } ;
static int radio_nr [ TYPHOON_MAX ] = { [ 0 . . . ( TYPHOON_MAX - 1 ) ] = - 1 } ;
2009-03-06 13:54:52 -03:00
static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ ;
2012-01-16 05:17:32 -03:00
module_param_array ( io , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( io , " I/O addresses of the Typhoon card (0x316 or 0x336) " ) ;
module_param_array ( radio_nr , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( radio_nr , " Radio device numbers " ) ;
2009-03-06 13:54:52 -03:00
module_param ( mutefreq , ulong , 0 ) ;
MODULE_PARM_DESC ( mutefreq , " Frequency used when muting the card (in kHz) " ) ;
struct typhoon {
2012-01-16 05:17:32 -03:00
struct radio_isa_card isa ;
2005-04-16 15:20:36 -07:00
int muted ;
} ;
2012-01-16 05:17:32 -03:00
static struct radio_isa_card * typhoon_alloc ( void )
2005-04-16 15:20:36 -07:00
{
2012-01-16 05:17:32 -03:00
struct typhoon * ty = kzalloc ( sizeof ( * ty ) , GFP_KERNEL ) ;
return ty ? & ty - > isa : NULL ;
2005-04-16 15:20:36 -07:00
}
2012-01-16 05:17:32 -03:00
static int typhoon_s_frequency ( struct radio_isa_card * isa , u32 freq )
2005-04-16 15:20:36 -07:00
{
unsigned long outval ;
unsigned long x ;
/*
* The frequency transfer curve is not linear . The best fit I could
* get is
*
* outval = - 155 + exp ( ( f + 15.55 ) * 0.057 ) )
*
* where frequency f is in MHz . Since we don ' t have exp in the kernel ,
* I approximate this function by a third order polynomial .
*
*/
2012-01-16 05:17:32 -03:00
x = freq / 160 ;
2005-04-16 15:20:36 -07:00
outval = ( x * x + 2500 ) / 5000 ;
outval = ( outval * x + 5000 ) / 10000 ;
outval - = ( 10 * x * x + 10433 ) / 20866 ;
outval + = 4 * x - 11505 ;
2012-01-16 05:17:32 -03:00
outb_p ( ( outval > > 8 ) & 0x01 , isa - > io + 4 ) ;
outb_p ( outval > > 9 , isa - > io + 6 ) ;
outb_p ( outval & 0xff , isa - > io + 8 ) ;
2007-04-24 08:40:06 -03:00
return 0 ;
}
2006-08-08 09:10:04 -03:00
2012-01-16 05:17:32 -03:00
static int typhoon_s_mute_volume ( struct radio_isa_card * isa , bool mute , int vol )
2007-04-24 08:40:06 -03:00
{
2012-01-16 05:17:32 -03:00
struct typhoon * ty = container_of ( isa , struct typhoon , isa ) ;
2006-08-08 09:10:04 -03:00
2012-01-16 05:17:32 -03:00
if ( mute )
vol = 0 ;
vol > > = 14 ; /* Map 16 bit to 2 bit */
vol & = 3 ;
outb_p ( vol / 2 , isa - > io ) ; /* Set the volume, high bit. */
outb_p ( vol % 2 , isa - > io + 2 ) ; /* Set the volume, low bit. */
2006-08-08 09:10:04 -03:00
2012-01-16 05:17:32 -03:00
if ( vol = = 0 & & ! ty - > muted ) {
ty - > muted = true ;
return typhoon_s_frequency ( isa , mutefreq < < 4 ) ;
2007-04-24 08:40:06 -03:00
}
2012-01-16 05:17:32 -03:00
if ( vol & & ty - > muted ) {
ty - > muted = false ;
return typhoon_s_frequency ( isa , isa - > freq ) ;
2007-04-24 08:40:06 -03:00
}
return 0 ;
2005-04-16 15:20:36 -07:00
}
2012-01-16 05:17:32 -03:00
static const struct radio_isa_ops typhoon_ops = {
. alloc = typhoon_alloc ,
. s_mute_volume = typhoon_s_mute_volume ,
. s_frequency = typhoon_s_frequency ,
2005-04-16 15:20:36 -07:00
} ;
2012-01-16 05:17:32 -03:00
static const int typhoon_ioports [ ] = { 0x316 , 0x336 } ;
static struct radio_isa_driver typhoon_driver = {
. driver = {
. match = radio_isa_match ,
. probe = radio_isa_probe ,
. remove = radio_isa_remove ,
. driver = {
. name = " radio-typhoon " ,
} ,
} ,
. io_params = io ,
. radio_nr_params = radio_nr ,
. io_ports = typhoon_ioports ,
. num_of_io_ports = ARRAY_SIZE ( typhoon_ioports ) ,
. region_size = 8 ,
. card = " Typhoon Radio " ,
. ops = & typhoon_ops ,
. has_stereo = true ,
. max_volume = 3 ,
2005-04-16 15:20:36 -07:00
} ;
2009-03-06 13:54:52 -03:00
static int __init typhoon_init ( void )
2008-04-10 21:34:39 -03:00
{
2012-01-16 05:17:32 -03:00
if ( mutefreq < 87000 | | mutefreq > 108000 ) {
printk ( KERN_ERR " %s: You must set a frequency (in kHz) used when muting the card, \n " ,
typhoon_driver . driver . driver . name ) ;
printk ( KERN_ERR " %s: e.g. with \" mutefreq=87500 \" (87000 <= mutefreq <= 108000) \n " ,
typhoon_driver . driver . driver . name ) ;
return - ENODEV ;
2009-03-06 13:54:52 -03:00
}
2012-01-16 05:17:32 -03:00
return isa_register_driver ( & typhoon_driver . driver , TYPHOON_MAX ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:54:52 -03:00
static void __exit typhoon_exit ( void )
2005-04-16 15:20:36 -07:00
{
2012-01-16 05:17:32 -03:00
isa_unregister_driver ( & typhoon_driver . driver ) ;
2005-04-16 15:20:36 -07:00
}
2012-01-16 05:17:32 -03:00
2005-04-16 15:20:36 -07:00
module_init ( typhoon_init ) ;
2009-03-06 13:54:52 -03:00
module_exit ( typhoon_exit ) ;
2005-04-16 15:20:36 -07:00