2006-04-08 23:06:16 +04:00
/* radio-trust.c - Trust FM Radio card driver for Linux 2.2
2005-04-17 02:20:36 +04:00
* by Eric Lammerts < eric @ scintilla . utwente . nl >
*
* Based on radio - aztech . c . Original notes :
*
2006-04-08 23:06:16 +04:00
* Adapted to support the Video for Linux API by
2005-04-17 02:20:36 +04:00
* Russell Kroll < rkroll @ exploits . org > . Based on original tuner code by :
*
* Quay Ly
* Donald Song
2006-04-08 23:06:16 +04:00
* Jason Lewis ( jlewis @ twilight . vtc . vsc . edu )
2005-04-17 02:20:36 +04:00
* Scott McGrath ( smcgrath @ twilight . vtc . vsc . edu )
* William McGrath ( wmcgrath @ twilight . vtc . vsc . edu )
*
* The basis for this code may be found at http : //bigbang.vtc.vsc.edu/fmradio/
*/
# include <stdarg.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <asm/io.h>
# include <asm/uaccess.h>
# include <linux/videodev.h>
2006-06-05 17:26:32 +04:00
# include <media/v4l2-common.h>
2005-04-17 02:20:36 +04:00
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
# ifndef CONFIG_RADIO_TRUST_PORT
# define CONFIG_RADIO_TRUST_PORT -1
# endif
2006-04-08 23:06:16 +04:00
static int io = CONFIG_RADIO_TRUST_PORT ;
2005-04-17 02:20:36 +04:00
static int radio_nr = - 1 ;
static int ioval = 0xf ;
static __u16 curvol ;
static __u16 curbass ;
static __u16 curtreble ;
static unsigned long curfreq ;
static int curstereo ;
static int curmute ;
/* i2c addresses */
# define TDA7318_ADDR 0x88
# define TSA6060T_ADDR 0xc4
# define TR_DELAY do { inb(io); inb(io); inb(io); } while(0)
# define TR_SET_SCL outb(ioval |= 2, io)
# define TR_CLR_SCL outb(ioval &= 0xfd, io)
# define TR_SET_SDA outb(ioval |= 1, io)
# define TR_CLR_SDA outb(ioval &= 0xfe, io)
static void write_i2c ( int n , . . . )
{
unsigned char val , mask ;
va_list args ;
va_start ( args , n ) ;
/* start condition */
TR_SET_SDA ;
TR_SET_SCL ;
TR_DELAY ;
TR_CLR_SDA ;
TR_CLR_SCL ;
TR_DELAY ;
for ( ; n ; n - - ) {
val = va_arg ( args , unsigned ) ;
for ( mask = 0x80 ; mask ; mask > > = 1 ) {
if ( val & mask )
TR_SET_SDA ;
else
TR_CLR_SDA ;
TR_SET_SCL ;
TR_DELAY ;
TR_CLR_SCL ;
TR_DELAY ;
}
/* acknowledge bit */
TR_SET_SDA ;
TR_SET_SCL ;
TR_DELAY ;
TR_CLR_SCL ;
TR_DELAY ;
}
/* stop condition */
TR_CLR_SDA ;
TR_DELAY ;
TR_SET_SCL ;
TR_DELAY ;
TR_SET_SDA ;
TR_DELAY ;
va_end ( args ) ;
}
static void tr_setvol ( __u16 vol )
{
curvol = vol / 2048 ;
write_i2c ( 2 , TDA7318_ADDR , curvol ^ 0x1f ) ;
}
static int basstreble2chip [ 15 ] = {
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 14 , 13 , 12 , 11 , 10 , 9 , 8
} ;
static void tr_setbass ( __u16 bass )
{
curbass = bass / 4370 ;
write_i2c ( 2 , TDA7318_ADDR , 0x60 | basstreble2chip [ curbass ] ) ;
}
static void tr_settreble ( __u16 treble )
{
curtreble = treble / 4370 ;
write_i2c ( 2 , TDA7318_ADDR , 0x70 | basstreble2chip [ curtreble ] ) ;
}
static void tr_setstereo ( int stereo )
{
curstereo = ! ! stereo ;
ioval = ( ioval & 0xfb ) | ( ! curstereo < < 2 ) ;
outb ( ioval , io ) ;
}
static void tr_setmute ( int mute )
{
curmute = ! ! mute ;
ioval = ( ioval & 0xf7 ) | ( curmute < < 3 ) ;
outb ( ioval , io ) ;
}
static int tr_getsigstr ( void )
{
int i , v ;
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
for ( i = 0 , v = 0 ; i < 100 ; i + + ) v | = inb ( io ) ;
return ( v & 1 ) ? 0 : 0xffff ;
}
static int tr_getstereo ( void )
{
/* don't know how to determine it, just return the setting */
return curstereo ;
}
static void tr_setfreq ( unsigned long f )
{
f / = 160 ; /* Convert to 10 kHz units */
f + = 1070 ; /* Add 10.7 MHz IF */
write_i2c ( 5 , TSA6060T_ADDR , ( f < < 1 ) | 1 , f > > 7 , 0x60 | ( ( f > > 15 ) & 1 ) , 0 ) ;
}
static int tr_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
switch ( cmd )
{
case VIDIOCGCAP :
{
struct video_capability * v = arg ;
memset ( v , 0 , sizeof ( * v ) ) ;
v - > type = VID_TYPE_TUNER ;
v - > channels = 1 ;
v - > audios = 1 ;
strcpy ( v - > name , " Trust FM Radio " ) ;
return 0 ;
}
case VIDIOCGTUNER :
{
struct video_tuner * v = arg ;
2006-04-08 23:06:16 +04:00
if ( v - > tuner ) /* Only 1 tuner */
2005-04-17 02:20:36 +04:00
return - EINVAL ;
v - > rangelow = 87500 * 16 ;
v - > rangehigh = 108000 * 16 ;
v - > flags = VIDEO_TUNER_LOW ;
v - > mode = VIDEO_MODE_AUTO ;
v - > signal = tr_getsigstr ( ) ;
if ( tr_getstereo ( ) )
v - > flags | = VIDEO_TUNER_STEREO_ON ;
strcpy ( v - > name , " FM " ) ;
return 0 ;
}
case VIDIOCSTUNER :
{
struct video_tuner * v = arg ;
if ( v - > tuner ! = 0 )
return - EINVAL ;
return 0 ;
}
case VIDIOCGFREQ :
{
unsigned long * freq = arg ;
* freq = curfreq ;
return 0 ;
}
case VIDIOCSFREQ :
{
unsigned long * freq = arg ;
tr_setfreq ( * freq ) ;
return 0 ;
}
case VIDIOCGAUDIO :
2006-04-08 23:06:16 +04:00
{
2005-04-17 02:20:36 +04:00
struct video_audio * v = arg ;
memset ( v , 0 , sizeof ( * v ) ) ;
v - > flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME |
2006-04-08 23:06:16 +04:00
VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE ;
2005-04-17 02:20:36 +04:00
v - > mode = curstereo ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO ;
v - > volume = curvol * 2048 ;
v - > step = 2048 ;
v - > bass = curbass * 4370 ;
v - > treble = curtreble * 4370 ;
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
strcpy ( v - > name , " Trust FM Radio " ) ;
2006-04-08 23:06:16 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
case VIDIOCSAUDIO :
{
struct video_audio * v = arg ;
2006-04-08 23:06:16 +04:00
if ( v - > audio )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-04-08 23:06:16 +04:00
tr_setvol ( v - > volume ) ;
2005-04-17 02:20:36 +04:00
tr_setbass ( v - > bass ) ;
tr_settreble ( v - > treble ) ;
tr_setstereo ( v - > mode & VIDEO_SOUND_STEREO ) ;
tr_setmute ( v - > flags & VIDEO_AUDIO_MUTE ) ;
return 0 ;
}
default :
return - ENOIOCTLCMD ;
}
}
static int tr_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return video_usercopy ( inode , file , cmd , arg , tr_do_ioctl ) ;
}
static struct file_operations trust_fops = {
. owner = THIS_MODULE ,
. open = video_exclusive_open ,
. release = video_exclusive_release ,
. ioctl = tr_ioctl ,
2006-01-09 20:24:57 +03:00
. compat_ioctl = v4l_compat_ioctl32 ,
2005-04-17 02:20:36 +04:00
. llseek = no_llseek ,
} ;
static struct video_device trust_radio =
{
. owner = THIS_MODULE ,
. name = " Trust FM Radio " ,
. type = VID_TYPE_TUNER ,
. hardware = VID_HARDWARE_TRUST ,
. fops = & trust_fops ,
} ;
static int __init trust_init ( void )
{
if ( io = = - 1 ) {
printk ( KERN_ERR " You must set an I/O address with io=0x??? \n " ) ;
return - EINVAL ;
}
if ( ! request_region ( io , 2 , " Trust FM Radio " ) ) {
printk ( KERN_ERR " trust: port 0x%x already in use \n " , io ) ;
return - EBUSY ;
}
if ( video_register_device ( & trust_radio , VFL_TYPE_RADIO , radio_nr ) = = - 1 )
{
release_region ( io , 2 ) ;
return - EINVAL ;
}
printk ( KERN_INFO " Trust FM Radio card driver v1.0. \n " ) ;
write_i2c ( 2 , TDA7318_ADDR , 0x80 ) ; /* speaker att. LF = 0 dB */
write_i2c ( 2 , TDA7318_ADDR , 0xa0 ) ; /* speaker att. RF = 0 dB */
write_i2c ( 2 , TDA7318_ADDR , 0xc0 ) ; /* speaker att. LR = 0 dB */
write_i2c ( 2 , TDA7318_ADDR , 0xe0 ) ; /* speaker att. RR = 0 dB */
write_i2c ( 2 , TDA7318_ADDR , 0x40 ) ; /* stereo 1 input, gain = 18.75 dB */
2006-04-08 23:06:16 +04:00
tr_setvol ( 0x8000 ) ;
2005-04-17 02:20:36 +04:00
tr_setbass ( 0x8000 ) ;
tr_settreble ( 0x8000 ) ;
tr_setstereo ( 1 ) ;
/* mute card - prevents noisy bootups */
tr_setmute ( 1 ) ;
return 0 ;
}
MODULE_AUTHOR ( " Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath " ) ;
MODULE_DESCRIPTION ( " A driver for the Trust FM Radio card. " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( io , int , 0 ) ;
MODULE_PARM_DESC ( io , " I/O address of the Trust FM Radio card (0x350 or 0x358) " ) ;
module_param ( radio_nr , int , 0 ) ;
static void __exit cleanup_trust_module ( void )
{
video_unregister_device ( & trust_radio ) ;
release_region ( io , 2 ) ;
}
module_init ( trust_init ) ;
module_exit ( cleanup_trust_module ) ;