2005-04-17 02:20:36 +04:00
/* Miro PCM20 radio driver for Linux radio support
* ( c ) 1998 Ruurd Reitsma < R . A . Reitsma @ wbmt . tudelft . nl >
* Thanks to Norberto Pellici for the ACI device interface specification
* The API part is based on the radiotrack driver by M . Kirkwood
* This driver relies on the aci mixer ( drivers / sound / aci . c )
* Look there for further info . . .
*/
/* Revision history:
*
* 1998 Ruurd Reitsma < R . A . Reitsma @ wbmt . tudelft . nl >
* 2000 - 09 - 05 Robert Siemer < Robert . Siemer @ gmx . de >
* removed unfinished volume control ( maybe adding it later again )
* use OSS - mixer ; added stereo control
*/
/* What ever you think about the ACI, version 0x07 is not very well!
* I can ' t get frequency , ' tuner status ' , ' tuner flags ' or mute / mono
2006-04-08 23:06:16 +04:00
* conditions . . . Robert
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/videodev.h>
2006-06-05 17:26:32 +04:00
# include <media/v4l2-common.h>
2006-05-24 01:39:29 +04:00
# include "oss/aci.h"
2005-04-17 02:20:36 +04:00
# include "miropcm20-rds-core.h"
static int radio_nr = - 1 ;
module_param ( radio_nr , int , 0 ) ;
struct pcm20_device {
unsigned long freq ;
int muted ;
int stereo ;
} ;
static int pcm20_mute ( struct pcm20_device * dev , unsigned char mute )
{
dev - > muted = mute ;
return aci_write_cmd ( ACI_SET_TUNERMUTE , mute ) ;
}
static int pcm20_stereo ( struct pcm20_device * dev , unsigned char stereo )
{
dev - > stereo = stereo ;
return aci_write_cmd ( ACI_SET_TUNERMONO , ! stereo ) ;
}
static int pcm20_setfreq ( struct pcm20_device * dev , unsigned long freq )
{
unsigned char freql ;
unsigned char freqh ;
dev - > freq = freq ;
freq / = 160 ;
if ( ! ( aci_version = = 0x07 | | aci_version > = 0xb0 ) )
freq / = 10 ; /* I don't know exactly which version
* needs this hack */
freql = freq & 0xff ;
freqh = freq > > 8 ;
aci_rds_cmd ( RDS_RESET , NULL , 0 ) ;
pcm20_stereo ( dev , 1 ) ;
return aci_rw_cmd ( ACI_WRITE_TUNE , freql , freqh ) ;
}
static int pcm20_getflags ( struct pcm20_device * dev , __u32 * flags , __u16 * signal )
{
/* okay, check for signal, stereo and rds here... */
int i ;
unsigned char buf ;
if ( ( i = aci_rw_cmd ( ACI_READ_TUNERSTATION , - 1 , - 1 ) ) < 0 )
return i ;
pr_debug ( " check_sig: 0x%x \n " , i ) ;
if ( i & 0x80 ) {
/* no signal from tuner */
* flags = 0 ;
* signal = 0 ;
return 0 ;
} else
* signal = 0xffff ;
if ( ( i = aci_rw_cmd ( ACI_READ_TUNERSTEREO , - 1 , - 1 ) ) < 0 )
return i ;
if ( i & 0x40 ) {
* flags = 0 ;
} else {
/* stereo */
* flags = VIDEO_TUNER_STEREO_ON ;
/* I can't see stereo, when forced to mono */
dev - > stereo = 1 ;
}
if ( ( i = aci_rds_cmd ( RDS_STATUS , & buf , 1 ) ) < 0 )
return i ;
if ( buf & 1 )
/* RDS available */
* flags | = VIDEO_TUNER_RDS_ON ;
else
return 0 ;
if ( ( i = aci_rds_cmd ( RDS_RXVALUE , & buf , 1 ) ) < 0 )
return i ;
pr_debug ( " rds-signal: %d \n " , buf ) ;
if ( buf > 15 ) {
printk ( " miropcm20-radio: RX strengths unexpected high... \n " ) ;
buf = 15 ;
}
/* refine signal */
if ( ( * signal = SCALE ( 15 , 0xffff , buf ) ) = = 0 )
* signal = 1 ;
return 0 ;
}
static int pcm20_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
struct video_device * dev = video_devdata ( file ) ;
struct pcm20_device * pcm20 = dev - > priv ;
int i ;
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
switch ( cmd )
{
case VIDIOCGCAP :
{
struct video_capability * v = arg ;
memset ( v , 0 , sizeof ( * v ) ) ;
v - > type = VID_TYPE_TUNER ;
strcpy ( v - > name , " Miro PCM20 " ) ;
v - > channels = 1 ;
v - > audios = 1 ;
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 = 87 * 16000 ;
v - > rangehigh = 108 * 16000 ;
pcm20_getflags ( pcm20 , & v - > flags , & v - > signal ) ;
v - > flags | = VIDEO_TUNER_LOW ;
v - > mode = VIDEO_MODE_AUTO ;
strcpy ( v - > name , " FM " ) ;
return 0 ;
}
case VIDIOCSTUNER :
{
struct video_tuner * v = arg ;
if ( v - > tuner ! = 0 )
return - EINVAL ;
/* Only 1 tuner so no setting needed ! */
return 0 ;
}
case VIDIOCGFREQ :
{
unsigned long * freq = arg ;
* freq = pcm20 - > freq ;
return 0 ;
}
case VIDIOCSFREQ :
{
unsigned long * freq = arg ;
pcm20 - > freq = * freq ;
i = pcm20_setfreq ( pcm20 , pcm20 - > freq ) ;
pr_debug ( " First view (setfreq): 0x%x \n " , i ) ;
return i ;
}
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 ;
if ( pcm20 - > muted )
v - > flags | = VIDEO_AUDIO_MUTE ;
v - > mode = VIDEO_SOUND_STEREO ;
if ( pcm20 - > stereo )
v - > mode | = VIDEO_SOUND_MONO ;
/* v->step=2048; */
strcpy ( v - > name , " 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 ;
pcm20_mute ( pcm20 , ! ! ( v - > flags & VIDEO_AUDIO_MUTE ) ) ;
if ( v - > flags & VIDEO_SOUND_MONO )
pcm20_stereo ( pcm20 , 0 ) ;
if ( v - > flags & VIDEO_SOUND_STEREO )
pcm20_stereo ( pcm20 , 1 ) ;
return 0 ;
}
default :
return - ENOIOCTLCMD ;
}
}
static int pcm20_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return video_usercopy ( inode , file , cmd , arg , pcm20_do_ioctl ) ;
}
static struct pcm20_device pcm20_unit = {
. freq = 87 * 16000 ,
. muted = 1 ,
} ;
2007-02-12 11:55:33 +03:00
static const struct file_operations pcm20_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = video_exclusive_open ,
. release = video_exclusive_release ,
. ioctl = pcm20_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 pcm20_radio = {
. owner = THIS_MODULE ,
. name = " Miro PCM 20 radio " ,
. type = VID_TYPE_TUNER ,
. fops = & pcm20_fops ,
. priv = & pcm20_unit
} ;
static int __init pcm20_init ( void )
{
if ( video_register_device ( & pcm20_radio , VFL_TYPE_RADIO , radio_nr ) = = - 1 )
goto video_register_device ;
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
if ( attach_aci_rds ( ) < 0 )
goto attach_aci_rds ;
printk ( KERN_INFO " Miro PCM20 radio card driver. \n " ) ;
return 0 ;
attach_aci_rds :
video_unregister_device ( & pcm20_radio ) ;
video_register_device :
return - EINVAL ;
}
MODULE_AUTHOR ( " Ruurd Reitsma " ) ;
MODULE_DESCRIPTION ( " A driver for the Miro PCM20 radio card. " ) ;
MODULE_LICENSE ( " GPL " ) ;
static void __exit pcm20_cleanup ( void )
{
unload_aci_rds ( ) ;
video_unregister_device ( & pcm20_radio ) ;
}
module_init ( pcm20_init ) ;
module_exit ( pcm20_cleanup ) ;