2005-04-16 15:20:36 -07:00
/*
* ALSA driver for TEA5757 / 5759 Philips AM / FM radio tuner chips
*
2007-10-15 09:50:19 +02:00
* Copyright ( c ) 2004 Jaroslav Kysela < perex @ perex . cz >
2005-04-16 15:20:36 -07:00
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
2008-11-09 15:25:31 -03:00
*/
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/init.h>
2009-02-27 11:51:24 -03:00
# include <linux/version.h>
2005-04-16 15:20:36 -07:00
# include <sound/core.h>
# include <sound/tea575x-tuner.h>
2007-10-15 09:50:19 +02:00
MODULE_AUTHOR ( " Jaroslav Kysela <perex@perex.cz> " ) ;
2005-04-16 15:20:36 -07:00
MODULE_DESCRIPTION ( " Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-02-27 11:51:24 -03:00
static int radio_nr = - 1 ;
module_param ( radio_nr , int , 0 ) ;
# define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
# define FREQ_LO (87 * 16000)
# define FREQ_HI (108 * 16000)
2005-04-16 15:20:36 -07:00
/*
* definitions
*/
# define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */
# define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */
# define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */
# define TEA575X_BIT_BAND_MASK (3<<20)
# define TEA575X_BIT_BAND_FM (0<<20)
# define TEA575X_BIT_BAND_MW (1<<20)
# define TEA575X_BIT_BAND_LW (1<<21)
# define TEA575X_BIT_BAND_SW (1<<22)
# define TEA575X_BIT_PORT_0 (1<<19) /* user bit */
# define TEA575X_BIT_PORT_1 (1<<18) /* user bit */
# define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */
# define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */
# define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */
# define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */
# define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */
# define TEA575X_BIT_DUMMY (1<<15) /* buffer */
# define TEA575X_BIT_FREQ_MASK 0x7fff
2009-02-27 11:51:24 -03:00
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 ,
}
} ;
2005-04-16 15:20:36 -07:00
/*
* lowlevel part
*/
2005-11-17 14:17:19 +01:00
static void snd_tea575x_set_freq ( struct snd_tea575x * tea )
2005-04-16 15:20:36 -07:00
{
unsigned long freq ;
freq = tea - > freq / 16 ; /* to kHz */
if ( freq > 108000 )
freq = 108000 ;
if ( freq < 87000 )
freq = 87000 ;
/* crystal fixup */
if ( tea - > tea5759 )
freq - = tea - > freq_fixup ;
else
freq + = tea - > freq_fixup ;
/* freq /= 12.5 */
freq * = 10 ;
freq / = 125 ;
tea - > val & = ~ TEA575X_BIT_FREQ_MASK ;
tea - > val | = freq & TEA575X_BIT_FREQ_MASK ;
tea - > ops - > write ( tea , tea - > val ) ;
}
/*
* Linux Video interface
*/
2009-02-27 11:51:24 -03:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
2005-04-16 15:20:36 -07:00
{
2008-08-23 08:32:09 -03:00
struct snd_tea575x * tea = video_drvdata ( file ) ;
2009-02-27 11:51:24 -03:00
strcpy ( v - > card , tea - > tea5759 ? " TEA5759 " : " TEA5757 " ) ;
strlcpy ( v - > driver , " tea575x-tuner " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " Maestro Radio " , sizeof ( v - > card ) ) ;
sprintf ( v - > bus_info , " PCI " ) ;
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 )
{
if ( v - > index > 0 )
return - EINVAL ;
strcpy ( v - > name , " FM " ) ;
v - > type = V4L2_TUNER_RADIO ;
v - > rangelow = FREQ_LO ;
v - > rangehigh = FREQ_HI ;
v - > rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO ;
v - > capability = V4L2_TUNER_CAP_LOW ;
v - > audmode = V4L2_TUNER_MODE_MONO ;
v - > signal = 0xffff ;
return 0 ;
}
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
if ( v - > index > 0 )
return - EINVAL ;
return 0 ;
}
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
struct snd_tea575x * tea = video_drvdata ( file ) ;
f - > type = V4L2_TUNER_RADIO ;
f - > frequency = tea - > freq ;
return 0 ;
}
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
struct snd_tea575x * tea = video_drvdata ( file ) ;
if ( f - > frequency < FREQ_LO | | f - > frequency > FREQ_HI )
return - EINVAL ;
tea - > freq = f - > frequency ;
snd_tea575x_set_freq ( tea ) ;
return 0 ;
}
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 ;
}
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 ) ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-02-27 11:51:24 -03:00
}
return - EINVAL ;
}
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
struct snd_tea575x * tea = video_drvdata ( file ) ;
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( tea - > ops - > mute ) {
ctrl - > value = tea - > mute ;
2008-11-09 15:25:31 -03:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-02-27 11:51:24 -03:00
}
return - EINVAL ;
}
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
struct snd_tea575x * tea = video_drvdata ( file ) ;
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( tea - > ops - > mute ) {
tea - > ops - > mute ( tea , ctrl - > value ) ;
tea - > mute = 1 ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
}
2009-02-27 11:51:24 -03:00
return - EINVAL ;
}
static int vidioc_g_input ( struct file * filp , void * priv , unsigned int * i )
{
* i = 0 ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-02-27 11:51:24 -03:00
static int vidioc_s_input ( struct file * filp , void * priv , unsigned int i )
2005-04-16 15:20:36 -07:00
{
2009-02-27 11:51:24 -03:00
if ( i ! = 0 )
return - EINVAL ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-12-30 06:58:20 -03:00
static int snd_tea575x_exclusive_open ( struct file * file )
2008-08-23 04:52:00 -03:00
{
2008-08-23 08:32:09 -03:00
struct snd_tea575x * tea = video_drvdata ( file ) ;
2008-08-23 04:52:00 -03:00
return test_and_set_bit ( 0 , & tea - > in_use ) ? - EBUSY : 0 ;
}
2008-12-30 06:58:20 -03:00
static int snd_tea575x_exclusive_release ( struct file * file )
2008-08-23 04:52:00 -03:00
{
2008-08-23 08:32:09 -03:00
struct snd_tea575x * tea = video_drvdata ( file ) ;
2008-08-23 04:52:00 -03:00
clear_bit ( 0 , & tea - > in_use ) ;
return 0 ;
}
2009-02-27 11:51:24 -03:00
static const struct v4l2_file_operations tea575x_fops = {
. owner = THIS_MODULE ,
. open = snd_tea575x_exclusive_open ,
. release = snd_tea575x_exclusive_release ,
. ioctl = video_ioctl2 ,
} ;
static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
. 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 ,
} ;
static struct video_device tea575x_radio = {
. name = " tea575x-tuner " ,
. fops = & tea575x_fops ,
. ioctl_ops = & tea575x_ioctl_ops ,
. release = video_device_release ,
} ;
2005-04-16 15:20:36 -07:00
/*
* initialize all the tea575x chips
*/
2005-11-17 14:17:19 +01:00
void snd_tea575x_init ( struct snd_tea575x * tea )
2005-04-16 15:20:36 -07:00
{
2009-02-27 11:51:24 -03:00
int retval ;
2005-04-16 15:20:36 -07:00
unsigned int val ;
2009-02-27 11:51:24 -03:00
struct video_device * tea575x_radio_inst ;
2005-04-16 15:20:36 -07:00
val = tea - > ops - > read ( tea ) ;
if ( val = = 0x1ffffff | | val = = 0 ) {
2009-02-27 11:51:24 -03:00
snd_printk ( KERN_ERR
" tea575x-tuner: Cannot find TEA575x chip \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2008-08-23 04:52:00 -03:00
tea - > in_use = 0 ;
2009-02-27 11:51:24 -03:00
tea - > val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40 ;
tea - > freq = 90500 * 16 ; /* 90.5Mhz default */
tea575x_radio_inst = video_device_alloc ( ) ;
if ( tea575x_radio_inst = = NULL ) {
printk ( KERN_ERR " tea575x-tuner: not enough memory \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2009-02-27 11:51:24 -03:00
memcpy ( tea575x_radio_inst , & tea575x_radio , sizeof ( tea575x_radio ) ) ;
strcpy ( tea575x_radio . name , tea - > tea5759 ?
" TEA5759 radio " : " TEA5757 radio " ) ;
video_set_drvdata ( tea575x_radio_inst , tea ) ;
retval = video_register_device ( tea575x_radio_inst ,
VFL_TYPE_RADIO , radio_nr ) ;
if ( retval ) {
printk ( KERN_ERR " tea575x-tuner: can't register video device! \n " ) ;
kfree ( tea575x_radio_inst ) ;
return ;
}
2005-04-16 15:20:36 -07:00
snd_tea575x_set_freq ( tea ) ;
2008-01-24 18:11:53 +01:00
/* mute on init */
2009-02-27 11:51:24 -03:00
if ( tea - > ops - > mute ) {
2008-01-24 18:11:53 +01:00
tea - > ops - > mute ( tea , 1 ) ;
2009-02-27 11:51:24 -03:00
tea - > mute = 1 ;
}
tea - > vd = tea575x_radio_inst ;
2005-04-16 15:20:36 -07:00
}
2005-11-17 14:17:19 +01:00
void snd_tea575x_exit ( struct snd_tea575x * tea )
2005-04-16 15:20:36 -07:00
{
2009-02-27 11:51:24 -03:00
if ( tea - > vd ) {
video_unregister_device ( tea - > vd ) ;
tea - > vd = NULL ;
2005-04-16 15:20:36 -07:00
}
}
static int __init alsa_tea575x_module_init ( void )
{
return 0 ;
}
2008-11-09 15:25:31 -03:00
2005-04-16 15:20:36 -07:00
static void __exit alsa_tea575x_module_exit ( void )
{
}
2008-11-09 15:25:31 -03:00
2005-04-16 15:20:36 -07:00
module_init ( alsa_tea575x_module_init )
module_exit ( alsa_tea575x_module_exit )
EXPORT_SYMBOL ( snd_tea575x_init ) ;
EXPORT_SYMBOL ( snd_tea575x_exit ) ;