2005-04-17 02:20:36 +04:00
/* SF16FMR2 radio driver for Linux radio support
* heavily based on fmi driver . . .
* ( c ) 2000 - 2002 Ziglio Frediano , freddy77 @ angelfire . com
*
* Notes on the hardware
*
* Frequency control is done digitally - - ie out ( port , encodefreq ( 95.8 ) ) ;
* No volume control - only mute / unmute - you have to use line volume
*
* For read stereo / mono you must wait 0.1 sec after set frequency and
* card unmuted so I set frequency on unmute
* Signal handling seem to work only on autoscanning ( not implemented )
2006-08-08 16:10:02 +04:00
*
* Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
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 */
2005-04-17 02:20:36 +04:00
# include <linux/delay.h> /* udelay */
2006-08-08 16:10:02 +04:00
# include <linux/videodev2.h> /* kernel radio structs */
2006-02-07 11:49:14 +03:00
# include <linux/mutex.h>
2009-03-06 19:53:26 +03:00
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
# include <linux/io.h> /* outb, outb_p */
# include <media/v4l2-device.h>
# include <media/v4l2-ioctl.h>
2005-04-17 02:20:36 +04:00
2009-03-06 19:53:26 +03:00
MODULE_AUTHOR ( " Ziglio Frediano, freddy77@angelfire.com " ) ;
MODULE_DESCRIPTION ( " A driver for the SF16FMR2 radio. " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int io = 0x384 ;
static int radio_nr = - 1 ;
module_param ( io , int , 0 ) ;
MODULE_PARM_DESC ( io , " I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284) " ) ;
module_param ( radio_nr , int , 0 ) ;
2006-08-08 16:10:06 +04:00
2006-08-08 16:10:02 +04:00
# define RADIO_VERSION KERNEL_VERSION(0,0,2)
2008-04-22 21:46:03 +04:00
# define AUD_VOL_INDEX 1
2005-04-17 02:20:36 +04:00
# undef DEBUG
//#define DEBUG 1
# ifdef DEBUG
# define debug_print(s) printk s
# else
# define debug_print(s)
# endif
/* this should be static vars for module size */
2009-03-06 19:53:26 +03:00
struct fmr2
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
struct v4l2_device v4l2_dev ;
struct video_device vdev ;
struct mutex lock ;
int io ;
2008-04-22 21:46:03 +04:00
int curvol ; /* 0-15 */
2005-04-17 02:20:36 +04:00
int mute ;
int stereo ; /* card is producing stereo audio */
unsigned long curfreq ; /* freq in kHz */
int card_type ;
} ;
2009-03-06 19:53:26 +03:00
static struct fmr2 fmr2_card ;
2005-04-17 02:20:36 +04:00
/* hw precision is 12.5 kHz
2009-05-02 19:04:13 +04:00
* It is only useful to give freq in interval of 200 ( = 0.0125 Mhz ) ,
2005-04-17 02:20:36 +04:00
* other bits will be truncated
*/
2009-03-06 19:53:26 +03:00
# define RSF16_ENCODE(x) ((x) / 200 + 856)
# define RSF16_MINFREQ (87 * 16000)
# define RSF16_MAXFREQ (108 * 16000)
2005-04-17 02:20:36 +04:00
2009-03-06 19:53:26 +03:00
static inline void wait ( int n , int io )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
for ( ; n ; - - n )
inb ( io ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:53:26 +03:00
static void outbits ( int bits , unsigned int data , int nWait , int io )
2005-04-17 02:20:36 +04:00
{
int bit ;
2009-03-06 19:53:26 +03:00
for ( ; - - bits > = 0 ; ) {
bit = ( data > > bits ) & 1 ;
outb ( bit , io ) ;
wait ( nWait , io ) ;
outb ( bit | 2 , io ) ;
wait ( nWait , io ) ;
outb ( bit , io ) ;
wait ( nWait , io ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-03-06 19:53:26 +03:00
static inline void fmr2_mute ( int io )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
outb ( 0x00 , io ) ;
wait ( 4 , io ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:53:26 +03:00
static inline void fmr2_unmute ( int io )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
outb ( 0x04 , io ) ;
wait ( 4 , io ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:53:26 +03:00
static inline int fmr2_stereo_mode ( int io )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
int n = inb ( io ) ;
outb ( 6 , io ) ;
inb ( io ) ;
n = ( ( n > > 3 ) & 1 ) ^ 1 ;
2005-04-17 02:20:36 +04:00
debug_print ( ( KERN_DEBUG " stereo: %d \n " , n ) ) ;
return n ;
}
2009-03-06 19:53:26 +03:00
static int fmr2_product_info ( struct fmr2 * dev )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
int n = inb ( dev - > io ) ;
2005-04-17 02:20:36 +04:00
n & = 0xC1 ;
2009-03-06 19:53:26 +03:00
if ( n = = 0 ) {
2005-04-17 02:20:36 +04:00
/* this should support volume set */
dev - > card_type = 12 ;
return 0 ;
}
/* not volume (mine is 11) */
2009-03-06 19:53:26 +03:00
dev - > card_type = ( n = = 128 ) ? 11 : 0 ;
2005-04-17 02:20:36 +04:00
return n ;
}
2009-03-06 19:53:26 +03:00
static inline int fmr2_getsigstr ( struct fmr2 * dev )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
/* !!! works only if scanning freq */
int res = 0xffff ;
outb ( 5 , dev - > io ) ;
wait ( 4 , dev - > io ) ;
if ( ! ( inb ( dev - > io ) & 1 ) )
res = 0 ;
2005-04-17 02:20:36 +04:00
debug_print ( ( KERN_DEBUG " signal: %d \n " , res ) ) ;
return res ;
}
/* set frequency and unmute card */
2009-03-06 19:53:26 +03:00
static int fmr2_setfreq ( struct fmr2 * dev )
2005-04-17 02:20:36 +04:00
{
unsigned long freq = dev - > curfreq ;
2009-03-06 19:53:26 +03:00
fmr2_mute ( dev - > io ) ;
2005-04-17 02:20:36 +04:00
/* 0x42 for mono output
* 0x102 forward scanning
* 0x182 scansione avanti
*/
2009-03-06 19:53:26 +03:00
outbits ( 9 , 0x2 , 3 , dev - > io ) ;
outbits ( 16 , RSF16_ENCODE ( freq ) , 2 , dev - > io ) ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:53:26 +03:00
fmr2_unmute ( dev - > io ) ;
2005-04-17 02:20:36 +04:00
/* wait 0.11 sec */
msleep ( 110 ) ;
/* NOTE if mute this stop radio
you must set freq on unmute */
2009-03-06 19:53:26 +03:00
dev - > stereo = fmr2_stereo_mode ( dev - > io ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* !!! not tested, in my card this does't work !!! */
2009-03-06 19:53:26 +03:00
static int fmr2_setvolume ( struct fmr2 * dev )
2005-04-17 02:20:36 +04:00
{
2008-04-22 21:46:03 +04:00
int vol [ 16 ] = { 0x021 , 0x084 , 0x090 , 0x104 ,
0x110 , 0x204 , 0x210 , 0x402 ,
0x404 , 0x408 , 0x410 , 0x801 ,
0x802 , 0x804 , 0x808 , 0x810 } ;
2009-03-06 19:53:26 +03:00
int i , a ;
2008-04-22 21:46:03 +04:00
int n = vol [ dev - > curvol & 0x0f ] ;
2005-04-17 02:20:36 +04:00
2008-04-22 21:46:03 +04:00
if ( dev - > card_type ! = 11 )
return 1 ;
2005-04-17 02:20:36 +04:00
2008-04-22 21:46:03 +04:00
for ( i = 12 ; - - i > = 0 ; ) {
2009-03-06 19:53:26 +03:00
a = ( ( n > > i ) & 1 ) < < 6 ; /* if (a==0) a = 0; else a = 0x40; */
outb ( a | 4 , dev - > io ) ;
wait ( 4 , dev - > io ) ;
outb ( a | 0x24 , dev - > io ) ;
wait ( 4 , dev - > io ) ;
outb ( a | 4 , dev - > io ) ;
wait ( 4 , dev - > io ) ;
2005-04-17 02:20:36 +04:00
}
2008-04-22 21:46:03 +04:00
for ( i = 6 ; - - i > = 0 ; ) {
2005-04-17 02:20:36 +04:00
a = ( ( 0x18 > > i ) & 1 ) < < 6 ;
2009-03-06 19:53:26 +03:00
outb ( a | 4 , dev - > io ) ;
wait ( 4 , dev - > io ) ;
outb ( a | 0x24 , dev - > io ) ;
wait ( 4 , dev - > io ) ;
outb ( a | 4 , dev - > io ) ;
wait ( 4 , dev - > io ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:53:26 +03:00
wait ( 4 , dev - > io ) ;
outb ( 0x14 , dev - > io ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-04-24 00:51:37 +04:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
2005-04-17 02:20:36 +04:00
{
2007-04-24 00:51:37 +04:00
strlcpy ( v - > driver , " radio-sf16fmr2 " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " SF16-FMR2 radio " , sizeof ( v - > card ) ) ;
2009-03-06 19:53:26 +03:00
strlcpy ( v - > bus_info , " ISA " , sizeof ( v - > bus_info ) ) ;
2007-04-24 00:51:37 +04:00
v - > version = RADIO_VERSION ;
2009-03-06 19:53:26 +03:00
v - > capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO ;
2007-04-24 00:51:37 +04:00
return 0 ;
}
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = video_drvdata ( file ) ;
2005-04-17 02:20:36 +04:00
2007-04-24 00:51:37 +04:00
if ( v - > index > 0 )
return - EINVAL ;
2009-03-06 19:53:26 +03:00
strlcpy ( v - > name , " FM " , sizeof ( v - > name ) ) ;
2007-04-24 00:51:37 +04:00
v - > type = V4L2_TUNER_RADIO ;
2009-05-02 19:04:13 +04:00
v - > rangelow = RSF16_MINFREQ ;
v - > rangehigh = RSF16_MAXFREQ ;
2009-05-02 19:06:15 +04:00
v - > rxsubchans = fmr2 - > stereo ? V4L2_TUNER_SUB_STEREO :
V4L2_TUNER_SUB_MONO ;
v - > capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW ;
v - > audmode = V4L2_TUNER_MODE_STEREO ;
2009-03-06 19:53:26 +03:00
mutex_lock ( & fmr2 - > lock ) ;
2007-04-24 00:51:37 +04:00
v - > signal = fmr2_getsigstr ( fmr2 ) ;
2009-03-06 19:53:26 +03:00
mutex_unlock ( & fmr2 - > lock ) ;
2007-04-24 00:51:37 +04:00
return 0 ;
}
2006-08-08 16:10:02 +04:00
2007-04-24 00:51:37 +04:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-03-06 19:53:26 +03:00
return v - > index ? - EINVAL : 0 ;
2007-04-24 00:51:37 +04:00
}
2006-08-08 16:10:02 +04:00
2007-04-24 00:51:37 +04:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = video_drvdata ( file ) ;
2006-08-08 16:10:02 +04:00
2007-04-24 00:51:37 +04:00
if ( f - > frequency < RSF16_MINFREQ | |
2009-03-06 19:53:26 +03:00
f - > frequency > RSF16_MAXFREQ )
2007-04-24 00:51:37 +04:00
return - EINVAL ;
2009-03-06 19:53:26 +03:00
/* rounding in steps of 200 to match the freq
that will be used */
fmr2 - > curfreq = ( f - > frequency / 200 ) * 200 ;
2007-04-24 00:51:37 +04:00
/* set card freq (if not muted) */
if ( fmr2 - > curvol & & ! fmr2 - > mute ) {
2009-03-06 19:53:26 +03:00
mutex_lock ( & fmr2 - > lock ) ;
2007-04-24 00:51:37 +04:00
fmr2_setfreq ( fmr2 ) ;
2009-03-06 19:53:26 +03:00
mutex_unlock ( & fmr2 - > lock ) ;
2007-04-24 00:51:37 +04:00
}
return 0 ;
}
2006-08-08 16:10:02 +04:00
2007-04-24 00:51:37 +04:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = video_drvdata ( file ) ;
2006-08-08 16:10:02 +04:00
2007-04-24 00:51:37 +04:00
f - > type = V4L2_TUNER_RADIO ;
f - > frequency = fmr2 - > curfreq ;
return 0 ;
}
2006-08-08 16:10:02 +04:00
2007-04-24 00:51:37 +04:00
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = video_drvdata ( file ) ;
2006-08-08 16:10:02 +04:00
2009-03-06 19:53:26 +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 :
/* Only card_type == 11 implements volume */
if ( fmr2 - > card_type = = 11 )
return v4l2_ctrl_query_fill ( qc , 0 , 15 , 1 , 0 ) ;
return v4l2_ctrl_query_fill ( qc , 0 , 1 , 1 , 0 ) ;
2007-04-24 00:51:37 +04:00
}
return - EINVAL ;
}
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = video_drvdata ( file ) ;
2007-04-24 00:51:37 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = fmr2 - > mute ;
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
ctrl - > value = fmr2 - > curvol ;
return 0 ;
}
return - EINVAL ;
}
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = video_drvdata ( file ) ;
2007-04-24 00:51:37 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
fmr2 - > mute = ctrl - > value ;
break ;
case V4L2_CID_AUDIO_VOLUME :
2009-03-06 19:53:26 +03:00
fmr2 - > curvol = ctrl - > value ;
2007-04-24 00:51:37 +04:00
break ;
default :
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2007-04-24 00:51:37 +04:00
if ( fmr2 - > curvol & & ! fmr2 - > mute )
printk ( KERN_DEBUG " unmute \n " ) ;
else
printk ( KERN_DEBUG " mute \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-08 16:10:02 +04:00
2009-03-06 19:53:26 +03:00
mutex_lock ( & fmr2 - > lock ) ;
2007-04-24 00:51:37 +04:00
if ( fmr2 - > curvol & & ! fmr2 - > mute ) {
fmr2_setvolume ( fmr2 ) ;
2008-04-22 21:46:03 +04:00
/* Set frequency and unmute card */
2007-04-24 00:51:37 +04:00
fmr2_setfreq ( fmr2 ) ;
} else
2009-03-06 19:53:26 +03:00
fmr2_mute ( fmr2 - > io ) ;
mutex_unlock ( & fmr2 - > lock ) ;
2007-04-24 00:51:37 +04:00
return 0 ;
}
static int vidioc_g_input ( struct file * filp , void * priv , unsigned int * i )
{
* i = 0 ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-04-24 00:51:37 +04:00
static int vidioc_s_input ( struct file * filp , void * priv , unsigned int i )
{
2009-03-06 19:53:26 +03:00
return i ? - EINVAL : 0 ;
2007-04-24 00:51:37 +04:00
}
2009-03-06 19:53:26 +03:00
static int vidioc_g_audio ( struct file * file , void * priv ,
2007-04-24 00:51:37 +04:00
struct v4l2_audio * a )
{
2009-03-06 19:53:26 +03:00
a - > index = 0 ;
strlcpy ( a - > name , " Radio " , sizeof ( a - > name ) ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
2007-04-24 00:51:37 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:53:26 +03:00
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
return a - > index ? - EINVAL : 0 ;
}
2005-04-17 02:20:36 +04:00
2008-12-30 12:58:20 +03:00
static const struct v4l2_file_operations fmr2_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
2007-04-24 00:51:37 +04:00
. ioctl = video_ioctl2 ,
2005-04-17 02:20:36 +04:00
} ;
2008-07-21 09:57:38 +04:00
static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
2007-04-24 00:51:37 +04:00
. vidioc_querycap = vidioc_querycap ,
. vidioc_g_tuner = vidioc_g_tuner ,
. vidioc_s_tuner = vidioc_s_tuner ,
. vidioc_g_audio = vidioc_g_audio ,
. vidioc_s_audio = vidioc_s_audio ,
. vidioc_g_input = vidioc_g_input ,
. vidioc_s_input = vidioc_s_input ,
. 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 ,
2005-04-17 02:20:36 +04:00
} ;
static int __init fmr2_init ( void )
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = & fmr2_card ;
struct v4l2_device * v4l2_dev = & fmr2 - > v4l2_dev ;
int res ;
strlcpy ( v4l2_dev - > name , " sf16fmr2 " , sizeof ( v4l2_dev - > name ) ) ;
fmr2 - > io = io ;
fmr2 - > stereo = 1 ;
mutex_init ( & fmr2 - > lock ) ;
if ( ! request_region ( fmr2 - > io , 2 , " sf16fmr2 " ) ) {
v4l2_err ( v4l2_dev , " request_region failed! \n " ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
2009-03-06 19:53:26 +03:00
res = v4l2_device_register ( NULL , v4l2_dev ) ;
if ( res < 0 ) {
release_region ( fmr2 - > io , 2 ) ;
v4l2_err ( v4l2_dev , " Could not register v4l2_device \n " ) ;
return res ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:53:26 +03:00
strlcpy ( fmr2 - > vdev . name , v4l2_dev - > name , sizeof ( fmr2 - > vdev . name ) ) ;
fmr2 - > vdev . v4l2_dev = v4l2_dev ;
fmr2 - > vdev . fops = & fmr2_fops ;
fmr2 - > vdev . ioctl_ops = & fmr2_ioctl_ops ;
fmr2 - > vdev . release = video_device_release_empty ;
video_set_drvdata ( & fmr2 - > vdev , fmr2 ) ;
2008-04-22 21:46:03 +04:00
2009-03-06 19:53:26 +03:00
if ( video_register_device ( & fmr2 - > vdev , VFL_TYPE_RADIO , radio_nr ) < 0 ) {
v4l2_device_unregister ( v4l2_dev ) ;
release_region ( fmr2 - > io , 2 ) ;
return - EINVAL ;
}
2008-04-22 21:46:03 +04:00
2009-03-06 19:53:26 +03:00
v4l2_info ( v4l2_dev , " SF16FMR2 radio card driver at 0x%x. \n " , fmr2 - > io ) ;
/* mute card - prevents noisy bootups */
mutex_lock ( & fmr2 - > lock ) ;
fmr2_mute ( fmr2 - > io ) ;
fmr2_product_info ( fmr2 ) ;
mutex_unlock ( & fmr2 - > lock ) ;
debug_print ( ( KERN_DEBUG " card_type %d \n " , fmr2 - > card_type ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-03-06 19:53:26 +03:00
static void __exit fmr2_exit ( void )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:53:26 +03:00
struct fmr2 * fmr2 = & fmr2_card ;
video_unregister_device ( & fmr2 - > vdev ) ;
v4l2_device_unregister ( & fmr2 - > v4l2_dev ) ;
release_region ( fmr2 - > io , 2 ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( fmr2_init ) ;
2009-03-06 19:53:26 +03:00
module_exit ( fmr2_exit ) ;
2005-04-17 02:20:36 +04:00
# ifndef MODULE
static int __init fmr2_setup_io ( char * str )
{
get_option ( & str , & io ) ;
return 1 ;
}
__setup ( " sf16fmr2= " , fmr2_setup_io ) ;
# endif