2006-04-08 23:06:16 +04:00
/* radio-aztech.c - Aztech radio card driver for Linux 2.2
2005-04-17 02:20:36 +04:00
*
2006-08-08 16:10:01 +04:00
* Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
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/
* along with more information on the card itself .
*
* History :
* 1999 - 02 - 24 Russell Kroll < rkroll @ exploits . org >
* Fine tuning / VIDEO_TUNER_LOW
* Range expanded to 87 - 108 MHz ( from 87.9 - 107.8 )
*
* Notable changes from the original source :
* - includes stripped down to the essentials
* - for loops used as delays replaced with udelay ( )
* - # defines removed , changed to static values
* - tuning structure changed - no more character arrays , other changes
*/
# 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 */
# include <asm/io.h> /* outb, outb_p */
# include <asm/uaccess.h> /* copy to/from user */
2006-08-08 16:10:01 +04:00
# include <linux/videodev2.h> /* kernel radio structs */
2006-06-05 17:26:32 +04:00
# include <media/v4l2-common.h>
2005-04-17 02:20:36 +04:00
2006-08-08 16:10:01 +04:00
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
# define RADIO_VERSION KERNEL_VERSION(0,0,2)
static struct v4l2_queryctrl radio_qctrl [ ] = {
{
. id = V4L2_CID_AUDIO_MUTE ,
. name = " Mute " ,
. minimum = 0 ,
. maximum = 1 ,
. default_value = 1 ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
} , {
. id = V4L2_CID_AUDIO_VOLUME ,
. name = " Volume " ,
. minimum = 0 ,
. maximum = 0xff ,
. step = 1 ,
. default_value = 0xff ,
. type = V4L2_CTRL_TYPE_INTEGER ,
}
} ;
2005-04-17 02:20:36 +04:00
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
# ifndef CONFIG_RADIO_AZTECH_PORT
# define CONFIG_RADIO_AZTECH_PORT -1
# endif
2006-04-08 23:06:16 +04:00
static int io = CONFIG_RADIO_AZTECH_PORT ;
2005-04-17 02:20:36 +04:00
static int radio_nr = - 1 ;
static int radio_wait_time = 1000 ;
2006-02-07 11:49:14 +03:00
static struct mutex lock ;
2005-04-17 02:20:36 +04:00
struct az_device
{
int curvol ;
unsigned long curfreq ;
int stereo ;
} ;
static int volconvert ( int level )
{
2006-04-08 23:06:16 +04:00
level > > = 14 ; /* Map 16bits down to 2 bit */
level & = 3 ;
2005-04-17 02:20:36 +04:00
/* convert to card-friendly values */
2006-04-08 23:06:16 +04:00
switch ( level )
2005-04-17 02:20:36 +04:00
{
2006-04-08 23:06:16 +04:00
case 0 :
2005-04-17 02:20:36 +04:00
return 0 ;
2006-04-08 23:06:16 +04:00
case 1 :
2005-04-17 02:20:36 +04:00
return 1 ;
case 2 :
return 4 ;
case 3 :
return 5 ;
}
return 0 ; /* Quieten gcc */
}
static void send_0_byte ( struct az_device * dev )
{
udelay ( radio_wait_time ) ;
outb_p ( 2 + volconvert ( dev - > curvol ) , io ) ;
outb_p ( 64 + 2 + volconvert ( dev - > curvol ) , io ) ;
}
static void send_1_byte ( struct az_device * dev )
{
udelay ( radio_wait_time ) ;
outb_p ( 128 + 2 + volconvert ( dev - > curvol ) , io ) ;
outb_p ( 128 + 64 + 2 + volconvert ( dev - > curvol ) , io ) ;
}
static int az_setvol ( struct az_device * dev , int vol )
{
2006-02-07 11:49:14 +03:00
mutex_lock ( & lock ) ;
2005-04-17 02:20:36 +04:00
outb ( volconvert ( vol ) , io ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* thanks to Michael Dwyer for giving me a dose of clues in
* the signal strength department . .
*
* This card has a stereo bit - bit 0 set = mono , not set = stereo
* It also has a " signal " bit - bit 1 set = bad signal , not set = good
*
*/
static int az_getsigstr ( struct az_device * dev )
{
if ( inb ( io ) & 2 ) /* bit set = no signal present */
return 0 ;
return 1 ; /* signal present */
}
static int az_getstereo ( struct az_device * dev )
{
if ( inb ( io ) & 1 ) /* bit set = mono */
return 0 ;
return 1 ; /* stereo */
}
static int az_setfreq ( struct az_device * dev , unsigned long frequency )
{
int i ;
frequency + = 171200 ; /* Add 10.7 MHz IF */
frequency / = 800 ; /* Convert to 50 kHz units */
2006-04-08 23:06:16 +04:00
2006-02-07 11:49:14 +03:00
mutex_lock ( & lock ) ;
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
send_0_byte ( dev ) ; /* 0: LSB of frequency */
for ( i = 0 ; i < 13 ; i + + ) /* : frequency bits (1-13) */
if ( frequency & ( 1 < < i ) )
send_1_byte ( dev ) ;
else
send_0_byte ( dev ) ;
send_0_byte ( dev ) ; /* 14: test bit - always 0 */
send_0_byte ( dev ) ; /* 15: test bit - always 0 */
send_0_byte ( dev ) ; /* 16: band data 0 - always 0 */
if ( dev - > stereo ) /* 17: stereo (1 to enable) */
send_1_byte ( dev ) ;
else
send_0_byte ( dev ) ;
send_1_byte ( dev ) ; /* 18: band data 1 - unknown */
send_0_byte ( dev ) ; /* 19: time base - always 0 */
send_0_byte ( dev ) ; /* 20: spacing (0 = 25 kHz) */
send_1_byte ( dev ) ; /* 21: spacing (1 = 25 kHz) */
send_0_byte ( dev ) ; /* 22: spacing (0 = 25 kHz) */
send_1_byte ( dev ) ; /* 23: AM/FM (FM = 1, always) */
/* latch frequency */
udelay ( radio_wait_time ) ;
outb_p ( 128 + 64 + volconvert ( dev - > curvol ) , io ) ;
2006-04-08 23:06:16 +04:00
2006-02-07 11:49:14 +03:00
mutex_unlock ( & lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-01-25 14:09:32 +03:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
{
strlcpy ( v - > driver , " radio-aztech " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " Aztech Radio " , sizeof ( v - > card ) ) ;
sprintf ( v - > bus_info , " ISA " ) ;
v - > version = RADIO_VERSION ;
v - > capabilities = V4L2_CAP_TUNER ;
return 0 ;
}
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
2005-04-17 02:20:36 +04:00
{
struct video_device * dev = video_devdata ( file ) ;
struct az_device * az = dev - > priv ;
2006-04-08 23:06:16 +04:00
2007-01-25 14:09:32 +03:00
if ( v - > index > 0 )
return - EINVAL ;
2006-08-08 16:10:01 +04:00
2007-01-25 14:09:32 +03:00
strcpy ( v - > name , " FM " ) ;
v - > type = V4L2_TUNER_RADIO ;
2006-08-08 16:10:01 +04:00
2007-01-25 14:09:32 +03:00
v - > rangelow = ( 87 * 16000 ) ;
v - > rangehigh = ( 108 * 16000 ) ;
v - > rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO ;
v - > capability = V4L2_TUNER_CAP_LOW ;
if ( az_getstereo ( az ) )
v - > audmode = V4L2_TUNER_MODE_STEREO ;
else
v - > audmode = V4L2_TUNER_MODE_MONO ;
v - > signal = 0xFFFF * az_getsigstr ( az ) ;
2006-08-08 16:10:01 +04:00
2007-01-25 14:09:32 +03:00
return 0 ;
}
2006-08-08 16:10:01 +04:00
2007-01-25 14:09:32 +03:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
if ( v - > index > 0 )
return - EINVAL ;
2006-08-08 16:10:01 +04:00
2007-01-25 14:09:32 +03:00
return 0 ;
}
2006-08-08 16:10:01 +04:00
2007-01-25 21:10:31 +03:00
static int vidioc_g_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
if ( a - > index > 1 )
return - EINVAL ;
strcpy ( a - > name , " Radio " ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
return 0 ;
}
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
if ( a - > index ! = 0 )
return - EINVAL ;
return 0 ;
}
2007-01-25 14:09:32 +03:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
struct video_device * dev = video_devdata ( file ) ;
struct az_device * az = dev - > priv ;
2006-08-08 16:10:01 +04:00
2007-01-25 14:09:32 +03:00
az - > curfreq = f - > frequency ;
az_setfreq ( az , az - > curfreq ) ;
return 0 ;
}
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
struct video_device * dev = video_devdata ( file ) ;
struct az_device * az = dev - > priv ;
f - > type = V4L2_TUNER_RADIO ;
f - > frequency = az - > curfreq ;
return 0 ;
}
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( radio_qctrl ) ; i + + ) {
if ( qc - > id & & qc - > id = = radio_qctrl [ i ] . id ) {
memcpy ( qc , & ( radio_qctrl [ i ] ) ,
sizeof ( * qc ) ) ;
return ( 0 ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-25 14:09:32 +03:00
}
return - EINVAL ;
}
2006-08-08 16:10:01 +04:00
2007-01-25 14:09:32 +03:00
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
struct video_device * dev = video_devdata ( file ) ;
struct az_device * az = dev - > priv ;
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( az - > curvol = = 0 )
ctrl - > value = 1 ;
else
ctrl - > value = 0 ;
return ( 0 ) ;
case V4L2_CID_AUDIO_VOLUME :
ctrl - > value = az - > curvol * 6554 ;
return ( 0 ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-25 14:09:32 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-01-25 14:09:32 +03:00
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
2005-04-17 02:20:36 +04:00
{
2007-01-25 14:09:32 +03:00
struct video_device * dev = video_devdata ( file ) ;
struct az_device * az = dev - > priv ;
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( ctrl - > value ) {
az_setvol ( az , 0 ) ;
} else {
az_setvol ( az , az - > curvol ) ;
}
return ( 0 ) ;
case V4L2_CID_AUDIO_VOLUME :
az_setvol ( az , ctrl - > value ) ;
return ( 0 ) ;
}
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
static struct az_device aztech_unit ;
2007-02-12 11:55:33 +03:00
static const struct file_operations aztech_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = video_exclusive_open ,
. release = video_exclusive_release ,
2007-01-25 14:09:32 +03:00
. ioctl = video_ioctl2 ,
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 aztech_radio =
{
2007-01-25 14:09:32 +03:00
. owner = THIS_MODULE ,
. name = " Aztech radio " ,
. type = VID_TYPE_TUNER ,
. hardware = 0 ,
. fops = & aztech_fops ,
. vidioc_querycap = vidioc_querycap ,
. vidioc_g_tuner = vidioc_g_tuner ,
. vidioc_s_tuner = vidioc_s_tuner ,
2007-01-25 21:10:31 +03:00
. vidioc_g_audio = vidioc_g_audio ,
. vidioc_s_audio = vidioc_s_audio ,
2007-01-25 14:09:32 +03:00
. 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
} ;
2007-01-25 14:09:32 +03:00
module_param_named ( debug , aztech_radio . debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " activates debug info " ) ;
2005-04-17 02:20:36 +04:00
static int __init aztech_init ( void )
{
if ( io = = - 1 )
{
printk ( KERN_ERR " You must set an I/O address with io=0x??? \n " ) ;
return - EINVAL ;
}
2006-04-08 23:06:16 +04:00
if ( ! request_region ( io , 2 , " aztech " ) )
2005-04-17 02:20:36 +04:00
{
printk ( KERN_ERR " aztech: port 0x%x already in use \n " , io ) ;
return - EBUSY ;
}
2006-02-07 11:49:14 +03:00
mutex_init ( & lock ) ;
2005-04-17 02:20:36 +04:00
aztech_radio . priv = & aztech_unit ;
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
if ( video_register_device ( & aztech_radio , VFL_TYPE_RADIO , radio_nr ) = = - 1 )
{
release_region ( io , 2 ) ;
return - EINVAL ;
}
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " Aztech radio card driver v1.00/19990224 rkroll@exploits.org \n " ) ;
/* mute card - prevents noisy bootups */
outb ( 0 , io ) ;
return 0 ;
}
MODULE_AUTHOR ( " Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath " ) ;
MODULE_DESCRIPTION ( " A driver for the Aztech radio card. " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( io , int , 0 ) ;
module_param ( radio_nr , int , 0 ) ;
MODULE_PARM_DESC ( io , " I/O address of the Aztech card (0x350 or 0x358) " ) ;
static void __exit aztech_cleanup ( void )
{
video_unregister_device ( & aztech_radio ) ;
release_region ( io , 2 ) ;
}
module_init ( aztech_init ) ;
module_exit ( aztech_cleanup ) ;