2006-04-08 16:06:16 -03:00
/* radio-trust.c - Trust FM Radio card driver for Linux 2.2
2005-04-16 15:20:36 -07:00
* by Eric Lammerts < eric @ scintilla . utwente . nl >
*
* Based on radio - aztech . c . Original notes :
*
2006-04-08 16:06:16 -03:00
* Adapted to support the Video for Linux API by
2005-04-16 15:20:36 -07:00
* Russell Kroll < rkroll @ exploits . org > . Based on original tuner code by :
*
* Quay Ly
* Donald Song
2006-04-08 16:06:16 -03:00
* Jason Lewis ( jlewis @ twilight . vtc . vsc . edu )
2005-04-16 15:20:36 -07:00
* Scott McGrath ( smcgrath @ twilight . vtc . vsc . edu )
* William McGrath ( wmcgrath @ twilight . vtc . vsc . edu )
*
2006-08-08 09:10:05 -03:00
* Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
2005-04-16 15:20:36 -07:00
*/
# include <stdarg.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/ioport.h>
2006-08-08 09:10:05 -03:00
# include <linux/videodev2.h>
2009-03-06 13:54:23 -03:00
# include <linux/io.h>
# include <media/v4l2-device.h>
2008-07-20 08:12:02 -03:00
# include <media/v4l2-ioctl.h>
2005-04-16 15:20:36 -07:00
2009-03-06 13:54:23 -03:00
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 " ) ;
2011-06-25 10:15:42 -03:00
MODULE_VERSION ( " 0.0.3 " ) ;
2006-08-08 09:10:05 -03:00
2005-04-16 15:20:36 -07:00
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
# ifndef CONFIG_RADIO_TRUST_PORT
# define CONFIG_RADIO_TRUST_PORT -1
# endif
2006-04-08 16:06:16 -03:00
static int io = CONFIG_RADIO_TRUST_PORT ;
2005-04-16 15:20:36 -07:00
static int radio_nr = - 1 ;
2009-03-06 13:54:23 -03:00
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 ) ;
struct trust {
struct v4l2_device v4l2_dev ;
struct video_device vdev ;
int io ;
int ioval ;
__u16 curvol ;
__u16 curbass ;
__u16 curtreble ;
int muted ;
unsigned long curfreq ;
int curstereo ;
int curmute ;
struct mutex lock ;
} ;
static struct trust trust_card ;
2005-04-16 15:20:36 -07:00
/* i2c addresses */
# define TDA7318_ADDR 0x88
# define TSA6060T_ADDR 0xc4
2009-03-06 13:54:23 -03:00
# define TR_DELAY do { inb(tr->io); inb(tr->io); inb(tr->io); } while (0)
# define TR_SET_SCL outb(tr->ioval |= 2, tr->io)
# define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->io)
# define TR_SET_SDA outb(tr->ioval |= 1, tr->io)
# define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->io)
2005-04-16 15:20:36 -07:00
2009-03-06 13:54:23 -03:00
static void write_i2c ( struct trust * tr , int n , . . . )
2005-04-16 15:20:36 -07:00
{
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 ) ;
}
2009-03-06 13:54:23 -03:00
static void tr_setvol ( struct trust * tr , __u16 vol )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:54:23 -03:00
mutex_lock ( & tr - > lock ) ;
tr - > curvol = vol / 2048 ;
write_i2c ( tr , 2 , TDA7318_ADDR , tr - > curvol ^ 0x1f ) ;
mutex_unlock ( & tr - > lock ) ;
2005-04-16 15:20:36 -07:00
}
static int basstreble2chip [ 15 ] = {
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 14 , 13 , 12 , 11 , 10 , 9 , 8
} ;
2009-03-06 13:54:23 -03:00
static void tr_setbass ( struct trust * tr , __u16 bass )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:54:23 -03:00
mutex_lock ( & tr - > lock ) ;
tr - > curbass = bass / 4370 ;
write_i2c ( tr , 2 , TDA7318_ADDR , 0x60 | basstreble2chip [ tr - > curbass ] ) ;
mutex_unlock ( & tr - > lock ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:54:23 -03:00
static void tr_settreble ( struct trust * tr , __u16 treble )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:54:23 -03:00
mutex_lock ( & tr - > lock ) ;
tr - > curtreble = treble / 4370 ;
write_i2c ( tr , 2 , TDA7318_ADDR , 0x70 | basstreble2chip [ tr - > curtreble ] ) ;
mutex_unlock ( & tr - > lock ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:54:23 -03:00
static void tr_setstereo ( struct trust * tr , int stereo )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:54:23 -03:00
mutex_lock ( & tr - > lock ) ;
tr - > curstereo = ! ! stereo ;
tr - > ioval = ( tr - > ioval & 0xfb ) | ( ! tr - > curstereo < < 2 ) ;
outb ( tr - > ioval , tr - > io ) ;
mutex_unlock ( & tr - > lock ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:54:23 -03:00
static void tr_setmute ( struct trust * tr , int mute )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:54:23 -03:00
mutex_lock ( & tr - > lock ) ;
tr - > curmute = ! ! mute ;
tr - > ioval = ( tr - > ioval & 0xf7 ) | ( tr - > curmute < < 3 ) ;
outb ( tr - > ioval , tr - > io ) ;
mutex_unlock ( & tr - > lock ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:54:23 -03:00
static int tr_getsigstr ( struct trust * tr )
2005-04-16 15:20:36 -07:00
{
int i , v ;
2006-04-08 16:06:16 -03:00
2009-03-06 13:54:23 -03:00
mutex_lock ( & tr - > lock ) ;
for ( i = 0 , v = 0 ; i < 100 ; i + + )
v | = inb ( tr - > io ) ;
mutex_unlock ( & tr - > lock ) ;
return ( v & 1 ) ? 0 : 0xffff ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:54:23 -03:00
static int tr_getstereo ( struct trust * tr )
2005-04-16 15:20:36 -07:00
{
/* don't know how to determine it, just return the setting */
2009-03-06 13:54:23 -03:00
return tr - > curstereo ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:54:23 -03:00
static void tr_setfreq ( struct trust * tr , unsigned long f )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:54:23 -03:00
mutex_lock ( & tr - > lock ) ;
tr - > curfreq = f ;
2005-04-16 15:20:36 -07:00
f / = 160 ; /* Convert to 10 kHz units */
2009-03-06 13:54:23 -03:00
f + = 1070 ; /* Add 10.7 MHz IF */
write_i2c ( tr , 5 , TSA6060T_ADDR , ( f < < 1 ) | 1 , f > > 7 , 0x60 | ( ( f > > 15 ) & 1 ) , 0 ) ;
mutex_unlock ( & tr - > lock ) ;
2005-04-16 15:20:36 -07:00
}
2007-04-20 18:22:19 -03:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
2005-04-16 15:20:36 -07:00
{
2007-04-20 18:22:19 -03:00
strlcpy ( v - > driver , " radio-trust " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " Trust FM Radio " , sizeof ( v - > card ) ) ;
2009-03-06 13:54:23 -03:00
strlcpy ( v - > bus_info , " ISA " , sizeof ( v - > bus_info ) ) ;
v - > capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO ;
2007-04-20 18:22:19 -03:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
2007-04-20 18:22:19 -03:00
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = video_drvdata ( file ) ;
2007-04-20 18:22:19 -03:00
if ( v - > index > 0 )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2009-03-06 13:54:23 -03:00
strlcpy ( v - > name , " FM " , sizeof ( v - > name ) ) ;
2007-04-20 18:22:19 -03:00
v - > type = V4L2_TUNER_RADIO ;
2009-03-06 13:54:23 -03:00
v - > rangelow = 87.5 * 16000 ;
v - > rangehigh = 108 * 16000 ;
v - > rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO ;
2007-04-20 18:22:19 -03:00
v - > capability = V4L2_TUNER_CAP_LOW ;
2009-03-06 13:54:23 -03:00
if ( tr_getstereo ( tr ) )
2007-04-20 18:22:19 -03:00
v - > audmode = V4L2_TUNER_MODE_STEREO ;
else
v - > audmode = V4L2_TUNER_MODE_MONO ;
2009-03-06 13:54:23 -03:00
v - > signal = tr_getsigstr ( tr ) ;
2007-04-20 18:22:19 -03:00
return 0 ;
}
2006-08-08 09:10:05 -03:00
2007-04-20 18:22:19 -03:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = video_drvdata ( file ) ;
2006-08-08 09:10:05 -03:00
2009-03-06 13:54:23 -03:00
if ( v - > index )
return - EINVAL ;
tr_setstereo ( tr , v - > audmode = = V4L2_TUNER_MODE_STEREO ) ;
2007-04-20 18:22:19 -03:00
return 0 ;
}
2006-08-08 09:10:05 -03:00
2007-04-20 18:22:19 -03:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = video_drvdata ( file ) ;
2009-11-27 04:33:25 -03:00
if ( f - > tuner ! = 0 | | f - > type ! = V4L2_TUNER_RADIO )
return - EINVAL ;
2009-03-06 13:54:23 -03:00
tr_setfreq ( tr , f - > frequency ) ;
2007-04-20 18:22:19 -03:00
return 0 ;
}
2006-08-08 09:10:05 -03:00
2007-04-20 18:22:19 -03:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = video_drvdata ( file ) ;
2009-11-27 04:33:25 -03:00
if ( f - > tuner ! = 0 )
return - EINVAL ;
2007-04-20 18:22:19 -03:00
f - > type = V4L2_TUNER_RADIO ;
2009-03-06 13:54:23 -03:00
f - > frequency = tr - > curfreq ;
2007-04-20 18:22:19 -03:00
return 0 ;
}
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
2009-03-06 13:54:23 -03:00
switch ( qc - > id ) {
case V4L2_CID_AUDIO_MUTE :
return v4l2_ctrl_query_fill ( qc , 0 , 1 , 1 , 1 ) ;
case V4L2_CID_AUDIO_VOLUME :
return v4l2_ctrl_query_fill ( qc , 0 , 65535 , 2048 , 65535 ) ;
case V4L2_CID_AUDIO_BASS :
case V4L2_CID_AUDIO_TREBLE :
return v4l2_ctrl_query_fill ( qc , 0 , 65535 , 4370 , 32768 ) ;
2007-04-20 18:22:19 -03:00
}
return - EINVAL ;
}
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = video_drvdata ( file ) ;
2007-04-20 18:22:19 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
2009-03-06 13:54:23 -03:00
ctrl - > value = tr - > curmute ;
2007-04-20 18:22:19 -03:00
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
2009-03-06 13:54:23 -03:00
ctrl - > value = tr - > curvol * 2048 ;
2007-04-20 18:22:19 -03:00
return 0 ;
case V4L2_CID_AUDIO_BASS :
2009-03-06 13:54:23 -03:00
ctrl - > value = tr - > curbass * 4370 ;
2007-04-20 18:22:19 -03:00
return 0 ;
case V4L2_CID_AUDIO_TREBLE :
2009-03-06 13:54:23 -03:00
ctrl - > value = tr - > curtreble * 4370 ;
2007-04-20 18:22:19 -03:00
return 0 ;
}
return - EINVAL ;
}
2006-08-08 09:10:05 -03:00
2007-04-20 18:22:19 -03:00
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = video_drvdata ( file ) ;
2007-04-20 18:22:19 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
2009-03-06 13:54:23 -03:00
tr_setmute ( tr , ctrl - > value ) ;
2007-04-20 18:22:19 -03:00
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
2009-03-06 13:54:23 -03:00
tr_setvol ( tr , ctrl - > value ) ;
2007-04-20 18:22:19 -03:00
return 0 ;
case V4L2_CID_AUDIO_BASS :
2009-03-06 13:54:23 -03:00
tr_setbass ( tr , ctrl - > value ) ;
2007-04-20 18:22:19 -03:00
return 0 ;
case V4L2_CID_AUDIO_TREBLE :
2009-03-06 13:54:23 -03:00
tr_settreble ( tr , ctrl - > value ) ;
2007-04-20 18:22:19 -03:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-04-20 18:22:19 -03:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2007-04-20 18:22:19 -03:00
static int vidioc_g_input ( struct file * filp , void * priv , unsigned int * i )
{
* i = 0 ;
return 0 ;
}
static int vidioc_s_input ( struct file * filp , void * priv , unsigned int i )
{
2009-03-06 13:54:23 -03:00
return i ? - EINVAL : 0 ;
}
static int vidioc_g_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
a - > index = 0 ;
strlcpy ( a - > name , " Radio " , sizeof ( a - > name ) ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
2007-04-20 18:22:19 -03:00
return 0 ;
}
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
2009-03-06 13:54:23 -03:00
return a - > index ? - EINVAL : 0 ;
2005-04-16 15:20:36 -07:00
}
2008-12-30 06:58:20 -03:00
static const struct v4l2_file_operations trust_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
2010-11-14 09:36:23 -03:00
. unlocked_ioctl = video_ioctl2 ,
2005-04-16 15:20:36 -07:00
} ;
2008-07-21 02:57:38 -03:00
static const struct v4l2_ioctl_ops trust_ioctl_ops = {
2007-04-20 18:22:19 -03:00
. vidioc_querycap = vidioc_querycap ,
. vidioc_g_tuner = vidioc_g_tuner ,
. vidioc_s_tuner = vidioc_s_tuner ,
. vidioc_g_frequency = vidioc_g_frequency ,
. vidioc_s_frequency = vidioc_s_frequency ,
. vidioc_queryctrl = vidioc_queryctrl ,
. vidioc_g_ctrl = vidioc_g_ctrl ,
. vidioc_s_ctrl = vidioc_s_ctrl ,
. vidioc_g_audio = vidioc_g_audio ,
. vidioc_s_audio = vidioc_s_audio ,
. vidioc_g_input = vidioc_g_input ,
. vidioc_s_input = vidioc_s_input ,
2005-04-16 15:20:36 -07:00
} ;
static int __init trust_init ( void )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = & trust_card ;
struct v4l2_device * v4l2_dev = & tr - > v4l2_dev ;
int res ;
strlcpy ( v4l2_dev - > name , " trust " , sizeof ( v4l2_dev - > name ) ) ;
tr - > io = io ;
tr - > ioval = 0xf ;
mutex_init ( & tr - > lock ) ;
if ( tr - > io = = - 1 ) {
2009-03-09 08:11:21 -03:00
v4l2_err ( v4l2_dev , " You must set an I/O address with io=0x0x350 or 0x358 \n " ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
2009-03-06 13:54:23 -03:00
if ( ! request_region ( tr - > io , 2 , " Trust FM Radio " ) ) {
v4l2_err ( v4l2_dev , " port 0x%x already in use \n " , tr - > io ) ;
2005-04-16 15:20:36 -07:00
return - EBUSY ;
}
2009-03-06 13:54:23 -03:00
res = v4l2_device_register ( NULL , v4l2_dev ) ;
if ( res < 0 ) {
release_region ( tr - > io , 2 ) ;
v4l2_err ( v4l2_dev , " Could not register v4l2_device \n " ) ;
return res ;
}
strlcpy ( tr - > vdev . name , v4l2_dev - > name , sizeof ( tr - > vdev . name ) ) ;
tr - > vdev . v4l2_dev = v4l2_dev ;
tr - > vdev . fops = & trust_fops ;
tr - > vdev . ioctl_ops = & trust_ioctl_ops ;
tr - > vdev . release = video_device_release_empty ;
video_set_drvdata ( & tr - > vdev , tr ) ;
write_i2c ( tr , 2 , TDA7318_ADDR , 0x80 ) ; /* speaker att. LF = 0 dB */
write_i2c ( tr , 2 , TDA7318_ADDR , 0xa0 ) ; /* speaker att. RF = 0 dB */
write_i2c ( tr , 2 , TDA7318_ADDR , 0xc0 ) ; /* speaker att. LR = 0 dB */
write_i2c ( tr , 2 , TDA7318_ADDR , 0xe0 ) ; /* speaker att. RR = 0 dB */
write_i2c ( tr , 2 , TDA7318_ADDR , 0x40 ) ; /* stereo 1 input, gain = 18.75 dB */
2005-04-16 15:20:36 -07:00
2009-03-06 13:54:23 -03:00
tr_setvol ( tr , 0xffff ) ;
tr_setbass ( tr , 0x8000 ) ;
tr_settreble ( tr , 0x8000 ) ;
tr_setstereo ( tr , 1 ) ;
2005-04-16 15:20:36 -07:00
/* mute card - prevents noisy bootups */
2009-03-06 13:54:23 -03:00
tr_setmute ( tr , 1 ) ;
2005-04-16 15:20:36 -07:00
2010-11-14 09:36:23 -03:00
if ( video_register_device ( & tr - > vdev , VFL_TYPE_RADIO , radio_nr ) < 0 ) {
v4l2_device_unregister ( v4l2_dev ) ;
release_region ( tr - > io , 2 ) ;
return - EINVAL ;
}
v4l2_info ( v4l2_dev , " Trust FM Radio card driver v1.0. \n " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static void __exit cleanup_trust_module ( void )
{
2009-03-06 13:54:23 -03:00
struct trust * tr = & trust_card ;
video_unregister_device ( & tr - > vdev ) ;
v4l2_device_unregister ( & tr - > v4l2_dev ) ;
release_region ( tr - > io , 2 ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( trust_init ) ;
module_exit ( cleanup_trust_module ) ;