2012-01-16 11:55:10 +04:00
/*
* AimsLab RadioTrack ( aka RadioVeveal ) driver
*
* Copyright 1997 M . Kirkwood
*
* Converted to the radio - isa framework by Hans Verkuil < hans . verkuil @ cisco . com >
2006-08-08 16:10:01 +04:00
* Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
2008-10-27 21:13:47 +03:00
* Converted to new API by Alan Cox < alan @ lxorguk . ukuu . org . uk >
2005-04-17 02:20:36 +04:00
* Various bugfixes and enhancements by Russell Kroll < rkroll @ exploits . org >
*
* Notes on the hardware ( reverse engineered from other peoples '
* reverse engineering of AIMS ' code : - )
*
* Frequency control is done digitally - - ie out ( port , encodefreq ( 95.8 ) ) ;
*
* The signal strength query is unsurprisingly inaccurate . And it seems
* to indicate that ( on my card , at least ) the frequency setting isn ' t
* too great . ( I have to tune up .025 MHz from what the freq should be
* to get a report that the thing is tuned . )
*
* Volume control is ( ugh ) analogue :
* out ( port , start_increasing_volume ) ;
* wait ( a_wee_while ) ;
* out ( port , stop_changing_the_volume ) ;
2006-04-08 23:06:16 +04:00
*
2012-01-16 11:55:10 +04:00
* Fully tested with the Keene USB FM Transmitter and the v4l2 - compliance tool .
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 */
2011-01-16 16:09:13 +03:00
# include <linux/delay.h> /* msleep */
2006-08-08 16:10:01 +04:00
# include <linux/videodev2.h> /* kernel radio structs */
2009-03-06 19:45:27 +03:00
# include <linux/io.h> /* outb, outb_p */
2012-02-29 12:50:27 +04:00
# include <linux/slab.h>
2009-03-06 19:45:27 +03:00
# include <media/v4l2-device.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2012-01-16 11:55:10 +04:00
# include <media/v4l2-ctrls.h>
# include "radio-isa.h"
2012-06-14 00:25:39 +04:00
# include "lm7000.h"
2005-04-17 02:20:36 +04:00
2012-01-16 11:55:10 +04:00
MODULE_AUTHOR ( " M. Kirkwood " ) ;
2009-03-06 19:45:27 +03:00
MODULE_DESCRIPTION ( " A driver for the RadioTrack/RadioReveal radio card. " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-01-16 11:55:10 +04:00
MODULE_VERSION ( " 1.0.0 " ) ;
2006-08-08 16:10:01 +04:00
2005-04-17 02:20:36 +04:00
# ifndef CONFIG_RADIO_RTRACK_PORT
# define CONFIG_RADIO_RTRACK_PORT -1
# endif
2012-01-16 11:55:10 +04:00
# define RTRACK_MAX 2
2005-04-17 02:20:36 +04:00
2012-01-16 11:55:10 +04:00
static int io [ RTRACK_MAX ] = { [ 0 ] = CONFIG_RADIO_RTRACK_PORT ,
[ 1 . . . ( RTRACK_MAX - 1 ) ] = - 1 } ;
static int radio_nr [ RTRACK_MAX ] = { [ 0 . . . ( RTRACK_MAX - 1 ) ] = - 1 } ;
2009-03-06 19:45:27 +03:00
2012-01-16 11:55:10 +04:00
module_param_array ( io , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( io , " I/O addresses of the RadioTrack card (0x20f or 0x30f) " ) ;
module_param_array ( radio_nr , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( radio_nr , " Radio device numbers " ) ;
struct rtrack {
struct radio_isa_card isa ;
2005-04-17 02:20:36 +04:00
int curvol ;
} ;
2012-06-25 18:06:54 +04:00
static struct radio_isa_card * rtrack_alloc ( void )
2005-04-17 02:20:36 +04:00
{
2012-06-25 18:06:54 +04:00
struct rtrack * rt = kzalloc ( sizeof ( struct rtrack ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
2012-06-25 18:06:54 +04:00
if ( rt )
rt - > curvol = 0xff ;
return rt ? & rt - > isa : NULL ;
}
2005-04-17 02:20:36 +04:00
2012-06-14 00:25:39 +04:00
# define AIMS_BIT_TUN_CE (1 << 0)
# define AIMS_BIT_TUN_CLK (1 << 1)
# define AIMS_BIT_TUN_DATA (1 << 2)
# define AIMS_BIT_VOL_CE (1 << 3)
# define AIMS_BIT_TUN_STRQ (1 << 4)
/* bit 5 is not connected */
# define AIMS_BIT_VOL_UP (1 << 6) /* active low */
# define AIMS_BIT_VOL_DN (1 << 7) /* active low */
2005-04-17 02:20:36 +04:00
2012-10-27 20:42:06 +04:00
static void rtrack_set_pins ( void * handle , u8 pins )
2012-06-25 18:06:54 +04:00
{
2012-06-14 00:25:39 +04:00
struct radio_isa_card * isa = handle ;
struct rtrack * rt = container_of ( isa , struct rtrack , isa ) ;
u8 bits = AIMS_BIT_VOL_DN | AIMS_BIT_VOL_UP | AIMS_BIT_TUN_STRQ ;
2005-04-17 02:20:36 +04:00
2012-06-14 00:25:39 +04:00
if ( ! v4l2_ctrl_g_ctrl ( rt - > isa . mute ) )
bits | = AIMS_BIT_VOL_CE ;
if ( pins & LM7000_DATA )
bits | = AIMS_BIT_TUN_DATA ;
if ( pins & LM7000_CLK )
bits | = AIMS_BIT_TUN_CLK ;
if ( pins & LM7000_CE )
bits | = AIMS_BIT_TUN_CE ;
outb_p ( bits , rt - > isa . io ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-16 11:55:10 +04:00
static int rtrack_s_frequency ( struct radio_isa_card * isa , u32 freq )
2005-04-17 02:20:36 +04:00
{
2012-06-14 00:25:39 +04:00
lm7000_set_freq ( freq , isa , rtrack_set_pins ) ;
2006-08-08 16:10:01 +04:00
2007-04-25 07:14:36 +04:00
return 0 ;
}
2006-08-08 16:10:01 +04:00
2012-01-16 11:55:10 +04:00
static u32 rtrack_g_signal ( struct radio_isa_card * isa )
2007-04-25 07:14:36 +04:00
{
2012-01-16 11:55:10 +04:00
/* bit set = no signal present */
return 0xffff * ! ( inb ( isa - > io ) & 2 ) ;
2007-04-25 07:14:36 +04:00
}
2006-08-08 16:10:01 +04:00
2012-01-16 11:55:10 +04:00
static int rtrack_s_mute_volume ( struct radio_isa_card * isa , bool mute , int vol )
2007-04-25 07:14:36 +04:00
{
2012-01-16 11:55:10 +04:00
struct rtrack * rt = container_of ( isa , struct rtrack , isa ) ;
int curvol = rt - > curvol ;
2006-08-08 16:10:01 +04:00
2012-01-16 11:55:10 +04:00
if ( mute ) {
outb ( 0xd0 , isa - > io ) ; /* volume steady + sigstr + off */
2007-04-25 07:14:36 +04:00
return 0 ;
}
2012-01-16 11:55:10 +04:00
if ( vol = = 0 ) { /* volume = 0 means mute the card */
outb ( 0x48 , isa - > io ) ; /* volume down but still "on" */
msleep ( curvol * 3 ) ; /* make sure it's totally down */
} else if ( curvol < vol ) {
outb ( 0x98 , isa - > io ) ; /* volume up + sigstr + on */
for ( ; curvol < vol ; curvol + + )
udelay ( 3000 ) ;
} else if ( curvol > vol ) {
outb ( 0x58 , isa - > io ) ; /* volume down + sigstr + on */
for ( ; curvol > vol ; curvol - - )
udelay ( 3000 ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-16 11:55:10 +04:00
outb ( 0xd8 , isa - > io ) ; /* volume steady + sigstr + on */
rt - > curvol = vol ;
2007-04-25 07:14:36 +04:00
return 0 ;
}
2012-01-16 11:55:10 +04:00
/* Mute card - prevents noisy bootups */
static int rtrack_initialize ( struct radio_isa_card * isa )
2007-04-25 07:14:36 +04:00
{
2012-01-16 11:55:10 +04:00
/* this ensures that the volume is all the way up */
outb ( 0x90 , isa - > io ) ; /* volume up but still "on" */
msleep ( 3000 ) ; /* make sure it's totally up */
outb ( 0xc0 , isa - > io ) ; /* steady volume, mute card */
2007-04-25 07:14:36 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2012-01-16 11:55:10 +04:00
static const struct radio_isa_ops rtrack_ops = {
. alloc = rtrack_alloc ,
. init = rtrack_initialize ,
. s_mute_volume = rtrack_s_mute_volume ,
. s_frequency = rtrack_s_frequency ,
. g_signal = rtrack_g_signal ,
2005-04-17 02:20:36 +04:00
} ;
2012-01-16 11:55:10 +04:00
static const int rtrack_ioports [ ] = { 0x20f , 0x30f } ;
static struct radio_isa_driver rtrack_driver = {
. driver = {
. match = radio_isa_match ,
. probe = radio_isa_probe ,
. remove = radio_isa_remove ,
. driver = {
. name = " radio-aimslab " ,
} ,
} ,
. io_params = io ,
. radio_nr_params = radio_nr ,
. io_ports = rtrack_ioports ,
. num_of_io_ports = ARRAY_SIZE ( rtrack_ioports ) ,
. region_size = 2 ,
. card = " AIMSlab RadioTrack/RadioReveal " ,
. ops = & rtrack_ops ,
. has_stereo = true ,
. max_volume = 0xff ,
2005-04-17 02:20:36 +04:00
} ;
static int __init rtrack_init ( void )
{
2012-01-16 11:55:10 +04:00
return isa_register_driver ( & rtrack_driver . driver , RTRACK_MAX ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:45:27 +03:00
static void __exit rtrack_exit ( void )
2005-04-17 02:20:36 +04:00
{
2012-01-16 11:55:10 +04:00
isa_unregister_driver ( & rtrack_driver . driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( rtrack_init ) ;
2009-03-06 19:45:27 +03:00
module_exit ( rtrack_exit ) ;