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 */
# 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"
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-01-16 11:55:10 +04:00
static struct radio_isa_card * rtrack_alloc ( void )
2005-04-17 02:20:36 +04:00
{
2012-01-16 11:55:10 +04:00
struct rtrack * rt = kzalloc ( sizeof ( struct rtrack ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
2012-01-16 11:55:10 +04:00
if ( rt )
rt - > curvol = 0xff ;
return rt ? & rt - > isa : NULL ;
2005-04-17 02:20:36 +04:00
}
2012-01-16 11:55:10 +04:00
/* The 128+64 on these outb's is to keep the volume stable while tuning.
* Without them , the volume _will_ creep up with each frequency change
* and bit 4 ( + 16 ) is to keep the signal strength meter enabled .
2005-04-17 02:20:36 +04:00
*/
2012-01-16 11:55:10 +04:00
static void send_0_byte ( struct radio_isa_card * isa , int on )
2005-04-17 02:20:36 +04:00
{
2012-01-16 11:55:10 +04:00
outb_p ( 128 + 64 + 16 + on + 1 , isa - > io ) ; /* wr-enable + data low */
outb_p ( 128 + 64 + 16 + on + 2 + 1 , isa - > io ) ; /* clock */
2011-01-06 13:16:04 +03:00
msleep ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-16 11:55:10 +04:00
static void send_1_byte ( struct radio_isa_card * isa , int on )
2005-04-17 02:20:36 +04:00
{
2012-01-16 11:55:10 +04:00
outb_p ( 128 + 64 + 16 + on + 4 + 1 , isa - > io ) ; /* wr-enable+data high */
outb_p ( 128 + 64 + 16 + on + 4 + 2 + 1 , isa - > io ) ; /* clock */
2011-01-06 13:16:04 +03:00
msleep ( 1 ) ;
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-01-16 11:55:10 +04:00
int on = v4l2_ctrl_g_ctrl ( isa - > mute ) ? 0 : 8 ;
2005-04-17 02:20:36 +04:00
int i ;
freq + = 171200 ; /* Add 10.7 MHz IF */
freq / = 800 ; /* Convert to 50 kHz units */
2006-04-08 23:06:16 +04:00
2012-01-16 11:55:10 +04:00
send_0_byte ( isa , on ) ; /* 0: LSB of frequency */
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 13 ; i + + ) /* : frequency bits (1-13) */
if ( freq & ( 1 < < i ) )
2012-01-16 11:55:10 +04:00
send_1_byte ( isa , on ) ;
2005-04-17 02:20:36 +04:00
else
2012-01-16 11:55:10 +04:00
send_0_byte ( isa , on ) ;
2006-04-08 23:06:16 +04:00
2012-01-16 11:55:10 +04:00
send_0_byte ( isa , on ) ; /* 14: test bit - always 0 */
send_0_byte ( isa , on ) ; /* 15: test bit - always 0 */
2006-08-08 16:10:01 +04:00
2012-01-16 11:55:10 +04:00
send_0_byte ( isa , on ) ; /* 16: band data 0 - always 0 */
send_0_byte ( isa , on ) ; /* 17: band data 1 - always 0 */
send_0_byte ( isa , on ) ; /* 18: band data 2 - always 0 */
send_0_byte ( isa , on ) ; /* 19: time base - always 0 */
2006-08-08 16:10:01 +04:00
2012-01-16 11:55:10 +04:00
send_0_byte ( isa , on ) ; /* 20: spacing (0 = 25 kHz) */
send_1_byte ( isa , on ) ; /* 21: spacing (1 = 25 kHz) */
send_0_byte ( isa , on ) ; /* 22: spacing (0 = 25 kHz) */
send_1_byte ( isa , on ) ; /* 23: AM/FM (FM = 1, always) */
2006-08-08 16:10:01 +04:00
2012-01-16 11:55:10 +04:00
outb ( 0xd0 + on , isa - > io ) ; /* volume steady + sigstr */
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 ) ;