2005-04-17 02:20:36 +04:00
/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
*
* GemTek hasn ' t released any specs on the card , so the protocol had to
* be reverse engineered with dosemu .
*
* Besides the protocol changes , this is mostly a copy of :
*
* RadioTrack II driver for Linux radio support ( C ) 1998 Ben Pfaff
2006-04-08 23:06:16 +04:00
*
2005-04-17 02:20:36 +04:00
* Based on RadioTrack I / RadioReveal ( C ) 1997 M . Kirkwood
2008-10-27 21:13:47 +03:00
* Converted to new API by Alan Cox < alan @ lxorguk . ukuu . org . uk >
2005-04-17 02:20:36 +04:00
* Various bugfixes and enhancements by Russell Kroll < rkroll @ exploits . org >
*
* TODO : Allow for more than one of these foolish entities : - )
*
2006-08-08 16:10:01 +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 */
# 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 */
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2006-06-05 17:26:32 +04:00
# include <media/v4l2-common.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
2007-10-01 07:27:55 +04:00
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
# define RADIO_VERSION KERNEL_VERSION(0,0,3)
# define RADIO_BANNER "GemTek Radio card driver: v0.0.3"
2006-08-08 16:10:01 +04:00
2007-10-01 07:27:55 +04:00
/*
* Module info .
*/
MODULE_AUTHOR ( " Jonas Munsin, Pekka Sepp<70> nen <pexu@kapsi.fi> " ) ;
MODULE_DESCRIPTION ( " A driver for the GemTek Radio card. " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* Module params .
*/
2006-08-08 16:10:01 +04:00
2005-04-17 02:20:36 +04:00
# ifndef CONFIG_RADIO_GEMTEK_PORT
# define CONFIG_RADIO_GEMTEK_PORT -1
# endif
2007-10-01 07:27:55 +04:00
# ifndef CONFIG_RADIO_GEMTEK_PROBE
# define CONFIG_RADIO_GEMTEK_PROBE 1
# endif
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
static int io = CONFIG_RADIO_GEMTEK_PORT ;
static int probe = CONFIG_RADIO_GEMTEK_PROBE ;
static int hardmute ;
static int shutdown = 1 ;
static int keepmuted = 1 ;
static int initmute = 1 ;
static int radio_nr = - 1 ;
2008-08-23 11:49:13 +04:00
static unsigned long in_use ;
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
module_param ( io , int , 0444 ) ;
2007-11-20 04:48:16 +03:00
MODULE_PARM_DESC ( io , " Force I/O port for the GemTek Radio card if automatic "
2007-10-01 07:27:55 +04:00
" probing is disabled or fails. The most common I/O ports are: 0x20c "
" 0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
2007-11-20 04:48:16 +03:00
" work for the combined sound/radiocard). " ) ;
2007-10-01 07:27:55 +04:00
module_param ( probe , bool , 0444 ) ;
MODULE_PARM_DESC ( probe , " Enable automatic device probing. Note: only the most "
" common I/O ports used by the card are probed. " ) ;
module_param ( hardmute , bool , 0644 ) ;
MODULE_PARM_DESC ( hardmute , " Enable `hard muting' by shutting down PLL, may "
" reduce static noise. " ) ;
module_param ( shutdown , bool , 0644 ) ;
MODULE_PARM_DESC ( shutdown , " Enable shutting down PLL and muting line when "
" module is unloaded. " ) ;
module_param ( keepmuted , bool , 0644 ) ;
MODULE_PARM_DESC ( keepmuted , " Keep card muted even when frequency is changed. " ) ;
module_param ( initmute , bool , 0444 ) ;
MODULE_PARM_DESC ( initmute , " Mute card when module is loaded. " ) ;
module_param ( radio_nr , int , 0444 ) ;
/*
* Functions for controlling the card .
*/
# define GEMTEK_LOWFREQ (87*16000)
# define GEMTEK_HIGHFREQ (108*16000)
2007-10-01 07:32:25 +04:00
/*
* Frequency calculation constants . Intermediate frequency 10.52 MHz ( nominal
* value 10.7 MHz ) , reference divisor 6.39 kHz ( nominal 6.25 kHz ) .
*/
# define FSCALE 8
# define IF_OFFSET ((unsigned int)(10.52 * 16000 * (1<<FSCALE)))
# define REF_FREQ ((unsigned int)(6.39 * 16 * (1<<FSCALE)))
2007-10-01 07:27:55 +04:00
# define GEMTEK_CK 0x01 /* Clock signal */
# define GEMTEK_DA 0x02 /* Serial data */
# define GEMTEK_CE 0x04 /* Chip enable */
# define GEMTEK_NS 0x08 /* No signal */
# define GEMTEK_MT 0x10 /* Line mute */
# define GEMTEK_STDF_3_125_KHZ 0x01 /* Standard frequency 3.125 kHz */
# define GEMTEK_PLL_OFF 0x07 /* PLL off */
# define BU2614_BUS_SIZE 32 /* BU2614 / BU2614FS bus size */
# define SHORT_DELAY 5 /* usec */
# define LONG_DELAY 75 /* usec */
struct gemtek_device {
unsigned long lastfreq ;
2005-04-17 02:20:36 +04:00
int muted ;
2007-10-01 07:38:30 +04:00
u32 bu2614data ;
2005-04-17 02:20:36 +04:00
} ;
2007-10-01 07:38:30 +04:00
# define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */
# define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */
# define BU2614_VOID_BITS 4 /* unused */
# define BU2614_FMES_BITS 1 /* CT, Frequency measurement beginning data */
# define BU2614_STDF_BITS 3 /* R0..R2, Standard frequency data */
# define BU2614_SWIN_BITS 1 /* S, Switch between FMIN / AMIN */
# define BU2614_SWAL_BITS 1 /* PS, Swallow counter division (AMIN only)*/
# define BU2614_VOID2_BITS 1 /* unused */
# define BU2614_FMUN_BITS 1 /* GT, Frequency measurement time & unlock */
# define BU2614_TEST_BITS 1 /* TS, Test data is input */
# define BU2614_FREQ_SHIFT 0
# define BU2614_PORT_SHIFT (BU2614_FREQ_BITS + BU2614_FREQ_SHIFT)
# define BU2614_VOID_SHIFT (BU2614_PORT_BITS + BU2614_PORT_SHIFT)
# define BU2614_FMES_SHIFT (BU2614_VOID_BITS + BU2614_VOID_SHIFT)
# define BU2614_STDF_SHIFT (BU2614_FMES_BITS + BU2614_FMES_SHIFT)
# define BU2614_SWIN_SHIFT (BU2614_STDF_BITS + BU2614_STDF_SHIFT)
# define BU2614_SWAL_SHIFT (BU2614_SWIN_BITS + BU2614_SWIN_SHIFT)
# define BU2614_VOID2_SHIFT (BU2614_SWAL_BITS + BU2614_SWAL_SHIFT)
# define BU2614_FMUN_SHIFT (BU2614_VOID2_BITS + BU2614_VOID2_SHIFT)
# define BU2614_TEST_SHIFT (BU2614_FMUN_BITS + BU2614_FMUN_SHIFT)
# define MKMASK(field) (((1<<BU2614_##field##_BITS) - 1) << \
BU2614_ # # field # # _SHIFT )
# define BU2614_PORT_MASK MKMASK(PORT)
# define BU2614_FREQ_MASK MKMASK(FREQ)
# define BU2614_VOID_MASK MKMASK(VOID)
# define BU2614_FMES_MASK MKMASK(FMES)
# define BU2614_STDF_MASK MKMASK(STDF)
# define BU2614_SWIN_MASK MKMASK(SWIN)
# define BU2614_SWAL_MASK MKMASK(SWAL)
# define BU2614_VOID2_MASK MKMASK(VOID2)
# define BU2614_FMUN_MASK MKMASK(FMUN)
# define BU2614_TEST_MASK MKMASK(TEST)
2007-10-01 07:27:55 +04:00
static struct gemtek_device gemtek_unit ;
static spinlock_t lock ;
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
/*
* Set data which will be sent to BU2614FS .
2005-04-17 02:20:36 +04:00
*/
2007-10-01 07:38:30 +04:00
# define gemtek_bu2614_set(dev, field, data) ((dev)->bu2614data = \
( ( dev ) - > bu2614data & ~ field # # _MASK ) | ( ( data ) < < field # # _SHIFT ) )
2007-10-01 07:27:55 +04:00
/*
* Transmit settings to BU2614FS over GemTek IC .
*/
static void gemtek_bu2614_transmit ( struct gemtek_device * dev )
{
int i , bit , q , mute ;
2005-04-17 02:20:36 +04:00
spin_lock ( & lock ) ;
2007-10-01 07:27:55 +04:00
mute = dev - > muted ? GEMTEK_MT : 0x00 ;
outb_p ( mute | GEMTEK_DA | GEMTEK_CK , io ) ;
udelay ( SHORT_DELAY ) ;
outb_p ( mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK , io ) ;
udelay ( LONG_DELAY ) ;
2007-10-01 07:38:30 +04:00
for ( i = 0 , q = dev - > bu2614data ; i < 32 ; i + + , q > > = 1 ) {
bit = ( q & 1 ) ? GEMTEK_DA : 0 ;
outb_p ( mute | GEMTEK_CE | bit , io ) ;
udelay ( SHORT_DELAY ) ;
outb_p ( mute | GEMTEK_CE | bit | GEMTEK_CK , io ) ;
udelay ( SHORT_DELAY ) ;
2007-10-01 07:27:55 +04:00
}
outb_p ( mute | GEMTEK_DA | GEMTEK_CK , io ) ;
udelay ( SHORT_DELAY ) ;
outb_p ( mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK , io ) ;
udelay ( LONG_DELAY ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & lock ) ;
}
2007-10-01 07:27:55 +04:00
/*
2007-10-01 07:32:25 +04:00
* Calculate divisor from FM - frequency for BU2614FS ( 3.125 KHz STDF expected ) .
2007-10-01 07:27:55 +04:00
*/
2007-10-01 07:32:25 +04:00
static unsigned long gemtek_convfreq ( unsigned long freq )
2005-04-17 02:20:36 +04:00
{
2007-10-01 07:32:25 +04:00
return ( ( freq < < FSCALE ) + IF_OFFSET + REF_FREQ / 2 ) / REF_FREQ ;
2007-10-01 07:27:55 +04:00
}
/*
* Set FM - frequency .
*/
static void gemtek_setfreq ( struct gemtek_device * dev , unsigned long freq )
{
if ( keepmuted & & hardmute & & dev - > muted )
2005-04-17 02:20:36 +04:00
return ;
2007-10-01 07:27:55 +04:00
if ( freq < GEMTEK_LOWFREQ )
freq = GEMTEK_LOWFREQ ;
else if ( freq > GEMTEK_HIGHFREQ )
freq = GEMTEK_HIGHFREQ ;
dev - > lastfreq = freq ;
2005-04-17 02:20:36 +04:00
dev - > muted = 0 ;
2007-10-01 07:27:55 +04:00
gemtek_bu2614_set ( dev , BU2614_PORT , 0 ) ;
gemtek_bu2614_set ( dev , BU2614_FMES , 0 ) ;
gemtek_bu2614_set ( dev , BU2614_SWIN , 0 ) ; /* FM-mode */
gemtek_bu2614_set ( dev , BU2614_SWAL , 0 ) ;
gemtek_bu2614_set ( dev , BU2614_FMUN , 1 ) ; /* GT bit set */
gemtek_bu2614_set ( dev , BU2614_TEST , 0 ) ;
gemtek_bu2614_set ( dev , BU2614_STDF , GEMTEK_STDF_3_125_KHZ ) ;
2007-10-01 07:32:25 +04:00
gemtek_bu2614_set ( dev , BU2614_FREQ , gemtek_convfreq ( freq ) ) ;
2007-10-01 07:27:55 +04:00
gemtek_bu2614_transmit ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-01 07:27:55 +04:00
/*
* Set mute flag .
*/
static void gemtek_mute ( struct gemtek_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-10-01 07:27:55 +04:00
int i ;
dev - > muted = 1 ;
if ( hardmute ) {
/* Turn off PLL, disable data output */
gemtek_bu2614_set ( dev , BU2614_PORT , 0 ) ;
gemtek_bu2614_set ( dev , BU2614_FMES , 0 ) ; /* CT bit off */
gemtek_bu2614_set ( dev , BU2614_SWIN , 0 ) ; /* FM-mode */
gemtek_bu2614_set ( dev , BU2614_SWAL , 0 ) ;
gemtek_bu2614_set ( dev , BU2614_FMUN , 0 ) ; /* GT bit off */
gemtek_bu2614_set ( dev , BU2614_TEST , 0 ) ;
gemtek_bu2614_set ( dev , BU2614_STDF , GEMTEK_PLL_OFF ) ;
gemtek_bu2614_set ( dev , BU2614_FREQ , 0 ) ;
gemtek_bu2614_transmit ( dev ) ;
} else {
spin_lock ( & lock ) ;
/* Read bus contents (CE, CK and DA). */
i = inb_p ( io ) ;
/* Write it back with mute flag set. */
outb_p ( ( i > > 5 ) | GEMTEK_MT , io ) ;
udelay ( SHORT_DELAY ) ;
spin_unlock ( & lock ) ;
}
2005-04-17 02:20:36 +04:00
}
2007-10-01 07:27:55 +04:00
/*
* Unset mute flag .
*/
static void gemtek_unmute ( struct gemtek_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-10-01 07:27:55 +04:00
int i ;
dev - > muted = 0 ;
if ( hardmute ) {
/* Turn PLL back on. */
gemtek_setfreq ( dev , dev - > lastfreq ) ;
} else {
spin_lock ( & lock ) ;
i = inb_p ( io ) ;
outb_p ( i > > 5 , io ) ;
udelay ( SHORT_DELAY ) ;
spin_unlock ( & lock ) ;
}
2005-04-17 02:20:36 +04:00
}
2007-10-01 07:27:55 +04:00
/*
* Get signal strength ( = stereo status ) .
*/
static inline int gemtek_getsigstr ( void )
2005-04-17 02:20:36 +04:00
{
2007-10-01 07:27:55 +04:00
return inb_p ( io ) & GEMTEK_NS ? 0 : 1 ;
}
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
/*
* Check if requested card acts like GemTek Radio card .
*/
static int gemtek_verify ( int port )
{
static int verified = - 1 ;
int i , q ;
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
if ( verified = = port )
return 1 ;
2005-04-17 02:20:36 +04:00
spin_lock ( & lock ) ;
2006-04-08 23:06:16 +04:00
2007-10-01 07:27:55 +04:00
q = inb_p ( port ) ; /* Read bus contents before probing. */
/* Try to turn on CE, CK and DA respectively and check if card responds
properly . */
for ( i = 0 ; i < 3 ; + + i ) {
outb_p ( 1 < < i , port ) ;
udelay ( SHORT_DELAY ) ;
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
if ( ( inb_p ( port ) & ( ~ GEMTEK_NS ) ) ! = ( 0x17 | ( 1 < < ( i + 5 ) ) ) ) {
spin_unlock ( & lock ) ;
return 0 ;
}
}
outb_p ( q > > 5 , port ) ; /* Write bus contents back. */
udelay ( SHORT_DELAY ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & lock ) ;
2007-10-01 07:27:55 +04:00
verified = port ;
2006-04-08 23:06:16 +04:00
2007-10-01 07:27:55 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2007-10-01 07:27:55 +04:00
/*
* Automatic probing for card .
*/
static int gemtek_probe ( void )
2005-04-17 02:20:36 +04:00
{
2007-10-01 07:27:55 +04:00
int ioports [ ] = { 0x20c , 0x30c , 0x24c , 0x34c , 0x248 , 0x28c } ;
int i ;
if ( ! probe ) {
printk ( KERN_INFO " Automatic device probing disabled. \n " ) ;
return - 1 ;
}
printk ( KERN_INFO " Automatic device probing enabled. \n " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( ioports ) ; + + i ) {
printk ( KERN_INFO " Trying I/O port 0x%x... \n " , ioports [ i ] ) ;
if ( ! request_region ( ioports [ i ] , 1 , " gemtek-probe " ) ) {
printk ( KERN_WARNING " I/O port 0x%x busy! \n " ,
ioports [ i ] ) ;
continue ;
}
if ( gemtek_verify ( ioports [ i ] ) ) {
printk ( KERN_INFO " Card found from I/O port "
" 0x%x! \n " , ioports [ i ] ) ;
release_region ( ioports [ i ] , 1 ) ;
io = ioports [ i ] ;
return io ;
}
release_region ( ioports [ i ] , 1 ) ;
}
printk ( KERN_ERR " Automatic probing failed! \n " ) ;
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2007-10-01 07:27:55 +04:00
/*
* Video 4 Linux stuff .
*/
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 = 65535 ,
. step = 65535 ,
. default_value = 0xff ,
. type = V4L2_CTRL_TYPE_INTEGER ,
}
} ;
2008-12-30 12:58:20 +03:00
static int gemtek_exclusive_open ( struct file * file )
2008-08-23 11:49:13 +04:00
{
return test_and_set_bit ( 0 , & in_use ) ? - EBUSY : 0 ;
}
2008-12-30 12:58:20 +03:00
static int gemtek_exclusive_release ( struct file * file )
2008-08-23 11:49:13 +04:00
{
clear_bit ( 0 , & in_use ) ;
return 0 ;
}
2008-12-30 12:58:20 +03:00
static const struct v4l2_file_operations gemtek_fops = {
2007-10-01 07:27:55 +04:00
. owner = THIS_MODULE ,
2008-08-23 11:49:13 +04:00
. open = gemtek_exclusive_open ,
. release = gemtek_exclusive_release ,
2007-10-01 07:27:55 +04:00
. ioctl = video_ioctl2 ,
} ;
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
2007-04-25 07:15:46 +04:00
{
strlcpy ( v - > driver , " radio-gemtek " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " GemTek " , sizeof ( v - > card ) ) ;
sprintf ( v - > bus_info , " ISA " ) ;
v - > version = RADIO_VERSION ;
v - > capabilities = V4L2_CAP_TUNER ;
return 0 ;
}
2007-10-01 07:27:55 +04:00
static int vidioc_g_tuner ( struct file * file , void * priv , struct v4l2_tuner * v )
2005-04-17 02:20:36 +04:00
{
2007-04-25 07:15:46 +04:00
if ( v - > index > 0 )
return - EINVAL ;
2006-08-08 16:10:01 +04:00
2007-04-25 07:15:46 +04:00
strcpy ( v - > name , " FM " ) ;
v - > type = V4L2_TUNER_RADIO ;
2007-10-01 07:27:55 +04:00
v - > rangelow = GEMTEK_LOWFREQ ;
v - > rangehigh = GEMTEK_HIGHFREQ ;
v - > capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO ;
v - > signal = 0xffff * gemtek_getsigstr ( ) ;
if ( v - > signal ) {
v - > audmode = V4L2_TUNER_MODE_STEREO ;
v - > rxsubchans = V4L2_TUNER_SUB_STEREO ;
} else {
v - > audmode = V4L2_TUNER_MODE_MONO ;
v - > rxsubchans = V4L2_TUNER_SUB_MONO ;
}
2007-04-25 07:15:46 +04:00
return 0 ;
}
2007-10-01 07:27:55 +04:00
static int vidioc_s_tuner ( struct file * file , void * priv , struct v4l2_tuner * v )
2007-04-25 07:15:46 +04:00
{
if ( v - > index > 0 )
return - EINVAL ;
return 0 ;
}
2006-08-08 16:10:01 +04:00
2007-04-25 07:15:46 +04:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
2007-10-01 07:27:55 +04:00
struct v4l2_frequency * f )
2007-04-25 07:15:46 +04:00
{
2008-08-23 15:32:09 +04:00
struct gemtek_device * rt = video_drvdata ( file ) ;
2006-08-08 16:10:01 +04:00
2007-10-01 07:27:55 +04:00
gemtek_setfreq ( rt , f - > frequency ) ;
2007-04-25 07:15:46 +04:00
return 0 ;
}
2006-08-08 16:10:01 +04:00
2007-04-25 07:15:46 +04:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
2007-10-01 07:27:55 +04:00
struct v4l2_frequency * f )
2007-04-25 07:15:46 +04:00
{
2008-08-23 15:32:09 +04:00
struct gemtek_device * rt = video_drvdata ( file ) ;
2006-08-08 16:10:01 +04:00
2007-04-25 07:15:46 +04:00
f - > type = V4L2_TUNER_RADIO ;
2007-10-01 07:27:55 +04:00
f - > frequency = rt - > lastfreq ;
2007-04-25 07:15:46 +04:00
return 0 ;
}
2006-08-08 16:10:01 +04:00
2007-04-25 07:15:46 +04:00
static int vidioc_queryctrl ( struct file * file , void * priv ,
2007-10-01 07:27:55 +04:00
struct v4l2_queryctrl * qc )
2007-04-25 07:15:46 +04:00
{
int i ;
2006-08-08 16:10:01 +04:00
2007-10-01 07:27:55 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( radio_qctrl ) ; + + i ) {
2007-04-25 07:15:46 +04:00
if ( qc - > id & & qc - > id = = radio_qctrl [ i ] . id ) {
2007-10-01 07:27:55 +04:00
memcpy ( qc , & ( radio_qctrl [ i ] ) , sizeof ( * qc ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-04-25 07:15:46 +04:00
}
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2007-04-25 07:15:46 +04:00
static int vidioc_g_ctrl ( struct file * file , void * priv ,
2007-10-01 07:27:55 +04:00
struct v4l2_control * ctrl )
2007-04-25 07:15:46 +04:00
{
2008-08-23 15:32:09 +04:00
struct gemtek_device * rt = video_drvdata ( file ) ;
2005-04-17 02:20:36 +04:00
2007-04-25 07:15:46 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = rt - > muted ;
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
if ( rt - > muted )
ctrl - > value = 0 ;
else
ctrl - > value = 65535 ;
return 0 ;
}
return - EINVAL ;
}
static int vidioc_s_ctrl ( struct file * file , void * priv ,
2007-10-01 07:27:55 +04:00
struct v4l2_control * ctrl )
2007-04-25 07:15:46 +04:00
{
2008-08-23 15:32:09 +04:00
struct gemtek_device * rt = video_drvdata ( file ) ;
2007-04-25 07:15:46 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( ctrl - > value )
gemtek_mute ( rt ) ;
else
gemtek_unmute ( rt ) ;
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
if ( ctrl - > value )
gemtek_unmute ( rt ) ;
else
gemtek_mute ( rt ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-04-25 07:15:46 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-10-01 07:27:55 +04:00
static int vidioc_g_audio ( struct file * file , void * priv , struct v4l2_audio * a )
2005-04-17 02:20:36 +04:00
{
2007-04-25 07:15:46 +04:00
if ( a - > index > 1 )
return - EINVAL ;
strcpy ( a - > name , " Radio " ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
return 0 ;
}
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 )
{
if ( i ! = 0 )
return - EINVAL ;
return 0 ;
}
2007-10-01 07:27:55 +04:00
static int vidioc_s_audio ( struct file * file , void * priv , struct v4l2_audio * a )
2007-04-25 07:15:46 +04:00
{
if ( a - > index ! = 0 )
return - EINVAL ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-07-21 09:57:38 +04:00
static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
2007-10-01 07:27:55 +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
} ;
2008-07-21 09:57:38 +04:00
static struct video_device gemtek_radio = {
2008-08-23 13:23:55 +04:00
. name = " GemTek Radio card " ,
. fops = & gemtek_fops ,
. ioctl_ops = & gemtek_ioctl_ops ,
. release = video_device_release_empty ,
2008-07-21 09:57:38 +04:00
} ;
2007-10-01 07:27:55 +04:00
/*
* Initialization / cleanup related stuff .
*/
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
/*
* Initilize card .
*/
2005-04-17 02:20:36 +04:00
static int __init gemtek_init ( void )
{
2007-10-01 07:27:55 +04:00
printk ( KERN_INFO RADIO_BANNER " \n " ) ;
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
spin_lock_init ( & lock ) ;
2006-04-08 23:06:16 +04:00
2007-10-01 07:27:55 +04:00
gemtek_probe ( ) ;
if ( io ) {
if ( ! request_region ( io , 1 , " gemtek " ) ) {
printk ( KERN_ERR " I/O port 0x%x already in use. \n " , io ) ;
return - EBUSY ;
}
if ( ! gemtek_verify ( io ) )
printk ( KERN_WARNING " Card at I/O port 0x%x does not "
" respond properly, check your "
" configuration. \n " , io ) ;
else
printk ( KERN_INFO " Using I/O port 0x%x. \n " , io ) ;
} else if ( probe ) {
printk ( KERN_ERR " Automatic probing failed and no "
" fixed I/O port defined. \n " ) ;
return - ENODEV ;
} else {
printk ( KERN_ERR " Automatic probing disabled but no fixed "
" I/O port defined. " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2008-08-23 14:24:07 +04:00
video_set_drvdata ( & gemtek_radio , & gemtek_unit ) ;
2007-10-01 07:27:55 +04:00
2008-09-04 00:11:58 +04:00
if ( video_register_device ( & gemtek_radio , VFL_TYPE_RADIO , radio_nr ) < 0 ) {
2007-10-01 07:27:55 +04:00
release_region ( io , 1 ) ;
return - EBUSY ;
}
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
/* Set defaults */
gemtek_unit . lastfreq = GEMTEK_LOWFREQ ;
2007-10-01 07:38:30 +04:00
gemtek_unit . bu2614data = 0 ;
2005-04-17 02:20:36 +04:00
2007-10-01 07:27:55 +04:00
if ( initmute )
gemtek_mute ( & gemtek_unit ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-10-01 07:27:55 +04:00
/*
* Module cleanup
*/
static void __exit gemtek_exit ( void )
2005-04-17 02:20:36 +04:00
{
2007-10-01 07:27:55 +04:00
if ( shutdown ) {
hardmute = 1 ; /* Turn off PLL */
gemtek_mute ( & gemtek_unit ) ;
} else {
printk ( KERN_INFO " Module unloaded but card not muted! \n " ) ;
}
2005-04-17 02:20:36 +04:00
video_unregister_device ( & gemtek_radio ) ;
2007-10-01 07:27:55 +04:00
release_region ( io , 1 ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( gemtek_init ) ;
2007-10-01 07:27:55 +04:00
module_exit ( gemtek_exit ) ;