2005-04-17 02:20:36 +04:00
/* Maestro PCI sound card radio driver for Linux support
* ( c ) 2000 A . Tlalka , atlka @ pg . gda . pl
* Notes on the hardware
*
2006-04-08 23:06:16 +04:00
* + Frequency control is done digitally
2005-04-17 02:20:36 +04:00
* + No volume control - only mute / unmute - you have to use Aux line volume
* control on Maestro card to set the volume
* + Radio status ( tuned / not_tuned and stereo / mono ) is valid some time after
* frequency setting ( > 100 ms ) and only when the radio is unmuted .
* version 0.02
* + io port is automatically detected - only the first radio is used
* version 0.03
* + thread access locking additions
* version 0.04
* + code improvements
* + VIDEO_TUNER_LOW is permanent
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>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/delay.h>
2009-03-06 19:51:08 +03:00
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
2005-04-17 02:20:36 +04:00
# include <linux/pci.h>
2006-08-08 16:10:02 +04:00
# include <linux/videodev2.h>
2009-03-06 19:51:08 +03:00
# include <linux/io.h>
# include <media/v4l2-device.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2006-02-07 11:49:14 +03:00
2009-03-06 19:51:08 +03:00
MODULE_AUTHOR ( " Adam Tlalka, atlka@pg.gda.pl " ) ;
MODULE_DESCRIPTION ( " Radio driver for the Maestro PCI sound card radio. " ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-08-08 16:10:02 +04:00
2009-03-06 19:51:08 +03:00
static int radio_nr = - 1 ;
module_param ( radio_nr , int , 0 ) ;
# define RADIO_VERSION KERNEL_VERSION(0, 0, 6)
# define DRIVER_VERSION "0.06"
2005-04-17 02:20:36 +04:00
2006-01-10 07:52:48 +03:00
# define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */
2005-04-17 02:20:36 +04:00
# define IO_MASK 4 / * mask register offset from GPIO_DATA
bits 1 = unmask write to given bit */
# define IO_DIR 8 / * direction register offset from GPIO_DATA
bits 0 / 1 = read / write direction */
2006-01-10 07:52:48 +03:00
# define GPIO6 0x0040 /* mask bits for GPIO lines */
# define GPIO7 0x0080
# define GPIO8 0x0100
# define GPIO9 0x0200
2005-04-17 02:20:36 +04:00
2006-01-10 07:52:48 +03:00
# define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */
# define STR_CLK GPIO7
# define STR_WREN GPIO8
# define STR_MOST GPIO9
2005-04-17 02:20:36 +04:00
# define FREQ_LO 50*16000
# define FREQ_HI 150*16000
2006-01-10 07:52:48 +03:00
# define FREQ_IF 171200 /* 10.7*16000 */
# define FREQ_STEP 200 /* 12.5*16 */
2005-04-17 02:20:36 +04:00
# define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
/ ( FREQ_STEP < < 2 ) ) < < 2 ) /* (x==fmhz*16*1000) -> bits */
# define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
2009-03-06 19:51:08 +03:00
struct maestro {
struct v4l2_device v4l2_dev ;
struct video_device vdev ;
struct pci_dev * pdev ;
struct mutex lock ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:51:08 +03:00
u16 io ; /* base of Maestro card radio io (GPIO_DATA)*/
u16 muted ; /* VIDEO_AUDIO_MUTE */
u16 stereo ; /* VIDEO_TUNER_STEREO_ON */
u16 tuned ; /* signal strength (0 or 0xffff) */
} ;
2008-08-23 11:49:13 +04:00
2009-03-06 19:51:08 +03:00
static inline struct maestro * to_maestro ( struct v4l2_device * v4l2_dev )
2008-08-23 11:49:13 +04:00
{
2009-03-06 19:51:08 +03:00
return container_of ( v4l2_dev , struct maestro , v4l2_dev ) ;
2008-08-23 11:49:13 +04:00
}
2009-03-06 19:51:08 +03:00
static u32 radio_bits_get ( struct maestro * dev )
2008-08-23 11:49:13 +04:00
{
2009-03-06 19:51:08 +03:00
u16 io = dev - > io , l , rdata ;
u32 data = 0 ;
2006-01-10 07:52:49 +03:00
u16 omask ;
2006-01-10 07:52:48 +03:00
2005-04-17 02:20:36 +04:00
omask = inw ( io + IO_MASK ) ;
outw ( ~ ( STR_CLK | STR_WREN ) , io + IO_MASK ) ;
outw ( 0 , io ) ;
udelay ( 16 ) ;
2009-03-06 19:51:08 +03:00
for ( l = 24 ; l - - ; ) {
2005-04-17 02:20:36 +04:00
outw ( STR_CLK , io ) ; /* HI state */
udelay ( 2 ) ;
2009-03-06 19:51:08 +03:00
if ( ! l )
2005-04-17 02:20:36 +04:00
dev - > tuned = inw ( io ) & STR_MOST ? 0 : 0xffff ;
outw ( 0 , io ) ; /* LO state */
udelay ( 2 ) ;
data < < = 1 ; /* shift data */
rdata = inw ( io ) ;
2009-03-06 19:51:08 +03:00
if ( ! l )
dev - > stereo = ( rdata & STR_MOST ) ? 0 : 1 ;
else if ( rdata & STR_DATA )
data + + ;
2005-04-17 02:20:36 +04:00
udelay ( 2 ) ;
}
2006-01-10 07:52:48 +03:00
2009-03-06 19:51:08 +03:00
if ( dev - > muted )
2005-04-17 02:20:36 +04:00
outw ( STR_WREN , io ) ;
2006-01-10 07:52:48 +03:00
2005-04-17 02:20:36 +04:00
udelay ( 4 ) ;
outw ( omask , io + IO_MASK ) ;
2006-01-10 07:52:48 +03:00
2005-04-17 02:20:36 +04:00
return data & 0x3ffe ;
}
2009-03-06 19:51:08 +03:00
static void radio_bits_set ( struct maestro * dev , u32 data )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:51:08 +03:00
u16 io = dev - > io , l , bits ;
2006-01-10 07:52:49 +03:00
u16 omask , odir ;
2006-01-10 07:52:48 +03:00
2005-04-17 02:20:36 +04:00
omask = inw ( io + IO_MASK ) ;
2009-03-06 19:51:08 +03:00
odir = ( inw ( io + IO_DIR ) & ~ STR_DATA ) | ( STR_CLK | STR_WREN ) ;
2005-04-17 02:20:36 +04:00
outw ( odir | STR_DATA , io + IO_DIR ) ;
outw ( ~ ( STR_DATA | STR_CLK | STR_WREN ) , io + IO_MASK ) ;
udelay ( 16 ) ;
2009-03-06 19:51:08 +03:00
for ( l = 25 ; l ; l - - ) {
bits = ( ( data > > 18 ) & STR_DATA ) | STR_WREN ;
2005-04-17 02:20:36 +04:00
data < < = 1 ; /* shift data */
outw ( bits , io ) ; /* start strobe */
udelay ( 2 ) ;
outw ( bits | STR_CLK , io ) ; /* HI level */
udelay ( 2 ) ;
outw ( bits , io ) ; /* LO level */
udelay ( 4 ) ;
}
2006-01-10 07:52:48 +03:00
2009-03-06 19:51:08 +03:00
if ( ! dev - > muted )
2005-04-17 02:20:36 +04:00
outw ( 0 , io ) ;
2006-01-10 07:52:48 +03:00
2005-04-17 02:20:36 +04:00
udelay ( 4 ) ;
outw ( omask , io + IO_MASK ) ;
outw ( odir , io + IO_DIR ) ;
msleep ( 125 ) ;
}
2007-04-26 23:44:55 +04:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
{
2009-03-06 19:51:08 +03:00
struct maestro * dev = video_drvdata ( file ) ;
2007-04-26 23:44:55 +04:00
strlcpy ( v - > driver , " radio-maestro " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " Maestro Radio " , sizeof ( v - > card ) ) ;
2009-03-06 19:51:08 +03:00
snprintf ( v - > bus_info , sizeof ( v - > bus_info ) , " PCI:%s " , pci_name ( dev - > pdev ) ) ;
2007-04-26 23:44:55 +04:00
v - > version = RADIO_VERSION ;
2009-03-06 19:51:08 +03:00
v - > capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO ;
2007-04-26 23:44:55 +04:00
return 0 ;
}
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:51:08 +03:00
struct maestro * dev = video_drvdata ( file ) ;
2006-01-10 07:52:48 +03:00
2007-04-26 23:44:55 +04:00
if ( v - > index > 0 )
return - EINVAL ;
2009-03-06 19:51:08 +03:00
mutex_lock ( & dev - > lock ) ;
radio_bits_get ( dev ) ;
2007-04-26 23:44:55 +04:00
2009-03-06 19:51:08 +03:00
strlcpy ( v - > name , " FM " , sizeof ( v - > name ) ) ;
2007-04-26 23:44:55 +04:00
v - > type = V4L2_TUNER_RADIO ;
v - > rangelow = FREQ_LO ;
v - > rangehigh = FREQ_HI ;
2009-03-06 19:51:08 +03:00
v - > rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO ;
2007-04-26 23:44:55 +04:00
v - > capability = V4L2_TUNER_CAP_LOW ;
2009-03-06 19:51:08 +03:00
if ( dev - > stereo )
2007-04-26 23:44:55 +04:00
v - > audmode = V4L2_TUNER_MODE_STEREO ;
else
v - > audmode = V4L2_TUNER_MODE_MONO ;
2009-03-06 19:51:08 +03:00
v - > signal = dev - > tuned ;
mutex_unlock ( & dev - > lock ) ;
2007-04-26 23:44:55 +04:00
return 0 ;
}
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-03-06 19:51:08 +03:00
return v - > index ? - EINVAL : 0 ;
2007-04-26 23:44:55 +04:00
}
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 19:51:08 +03:00
struct maestro * dev = video_drvdata ( file ) ;
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
if ( f - > frequency < FREQ_LO | | f - > frequency > FREQ_HI )
return - EINVAL ;
2009-03-06 19:51:08 +03:00
mutex_lock ( & dev - > lock ) ;
radio_bits_set ( dev , FREQ2BITS ( f - > frequency ) ) ;
mutex_unlock ( & dev - > lock ) ;
2007-04-26 23:44:55 +04:00
return 0 ;
}
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 19:51:08 +03:00
struct maestro * dev = video_drvdata ( file ) ;
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
f - > type = V4L2_TUNER_RADIO ;
2009-03-06 19:51:08 +03:00
mutex_lock ( & dev - > lock ) ;
f - > frequency = BITS2FREQ ( radio_bits_get ( dev ) ) ;
mutex_unlock ( & dev - > lock ) ;
2007-04-26 23:44:55 +04:00
return 0 ;
}
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
2009-03-06 19:51:08 +03:00
switch ( qc - > id ) {
case V4L2_CID_AUDIO_MUTE :
return v4l2_ctrl_query_fill ( qc , 0 , 1 , 1 , 1 ) ;
2007-04-26 23:44:55 +04:00
}
return - EINVAL ;
}
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 19:51:08 +03:00
struct maestro * dev = video_drvdata ( file ) ;
2006-08-08 16:10:02 +04:00
2007-04-26 23:44:55 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
2009-03-06 19:51:08 +03:00
ctrl - > value = dev - > muted ;
2007-04-26 23:44:55 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-04-26 23:44:55 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-04-26 23:44:55 +04:00
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:51:08 +03:00
struct maestro * dev = video_drvdata ( file ) ;
u16 io = dev - > io ;
u16 omask ;
2007-04-26 23:44:55 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
2009-03-06 19:51:08 +03:00
mutex_lock ( & dev - > lock ) ;
omask = inw ( io + IO_MASK ) ;
2007-04-26 23:44:55 +04:00
outw ( ~ STR_WREN , io + IO_MASK ) ;
2009-03-06 19:51:08 +03:00
dev - > muted = ctrl - > value ;
outw ( dev - > muted ? STR_WREN : 0 , io ) ;
2007-04-26 23:44:55 +04:00
udelay ( 4 ) ;
outw ( omask , io + IO_MASK ) ;
msleep ( 125 ) ;
2009-03-06 19:51:08 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-04-26 23:44:55 +04:00
return 0 ;
}
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2009-03-06 19:51:08 +03:00
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 )
{
return i ? - EINVAL : 0 ;
}
2007-04-26 23:44:55 +04:00
static int vidioc_g_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
2009-03-06 19:51:08 +03:00
a - > index = 0 ;
strlcpy ( a - > name , " Radio " , sizeof ( a - > name ) ) ;
2007-04-26 23:44:55 +04:00
a - > capability = V4L2_AUDCAP_STEREO ;
return 0 ;
}
2006-01-10 07:52:48 +03:00
2009-03-06 19:51:08 +03:00
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
2007-04-26 23:44:55 +04:00
{
2009-03-06 19:51:08 +03:00
return a - > index ? - EINVAL : 0 ;
2007-04-26 23:44:55 +04:00
}
2009-03-06 19:51:08 +03:00
static const struct v4l2_file_operations maestro_fops = {
. owner = THIS_MODULE ,
. ioctl = video_ioctl2 ,
} ;
static const struct v4l2_ioctl_ops maestro_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 u16 __devinit radio_power_on ( struct maestro * dev )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:52:49 +03:00
register u16 io = dev - > io ;
register u32 ofreq ;
u16 omask , odir ;
2006-01-10 07:52:48 +03:00
2005-04-17 02:20:36 +04:00
omask = inw ( io + IO_MASK ) ;
2006-01-10 07:52:48 +03:00
odir = ( inw ( io + IO_DIR ) & ~ STR_DATA ) | ( STR_CLK | STR_WREN ) ;
2005-04-17 02:20:36 +04:00
outw ( odir & ~ STR_WREN , io + IO_DIR ) ;
2006-08-08 16:10:02 +04:00
dev - > muted = inw ( io ) & STR_WREN ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
outw ( odir , io + IO_DIR ) ;
outw ( ~ ( STR_WREN | STR_CLK ) , io + IO_MASK ) ;
outw ( dev - > muted ? 0 : STR_WREN , io ) ;
udelay ( 16 ) ;
outw ( omask , io + IO_MASK ) ;
ofreq = radio_bits_get ( dev ) ;
2006-01-10 07:52:48 +03:00
if ( ( ofreq < FREQ2BITS ( FREQ_LO ) ) | | ( ofreq > FREQ2BITS ( FREQ_HI ) ) )
2005-04-17 02:20:36 +04:00
ofreq = FREQ2BITS ( FREQ_LO ) ;
radio_bits_set ( dev , ofreq ) ;
2006-01-10 07:52:48 +03:00
2005-04-17 02:20:36 +04:00
return ( ofreq = = radio_bits_get ( dev ) ) ;
}
2006-01-10 07:52:47 +03:00
static int __devinit maestro_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:51:08 +03:00
struct maestro * dev ;
struct v4l2_device * v4l2_dev ;
2006-01-10 07:52:47 +03:00
int retval ;
retval = pci_enable_device ( pdev ) ;
if ( retval ) {
dev_err ( & pdev - > dev , " enabling pci device failed! \n " ) ;
goto err ;
}
retval = - ENOMEM ;
2009-03-06 19:51:08 +03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( dev = = NULL ) {
2006-01-10 07:52:47 +03:00
dev_err ( & pdev - > dev , " not enough memory \n " ) ;
goto err ;
}
2009-03-06 19:51:08 +03:00
v4l2_dev = & dev - > v4l2_dev ;
mutex_init ( & dev - > lock ) ;
dev - > pdev = pdev ;
2006-01-10 07:52:47 +03:00
2009-03-06 19:51:08 +03:00
strlcpy ( v4l2_dev - > name , " maestro " , sizeof ( v4l2_dev - > name ) ) ;
retval = v4l2_device_register ( & pdev - > dev , v4l2_dev ) ;
if ( retval < 0 ) {
v4l2_err ( v4l2_dev , " Could not register v4l2_device \n " ) ;
2006-01-10 07:52:47 +03:00
goto errfr ;
}
2009-03-06 19:51:08 +03:00
dev - > io = pci_resource_start ( pdev , 0 ) + GPIO_DATA ;
2006-01-10 07:52:47 +03:00
2009-03-06 19:51:08 +03:00
strlcpy ( dev - > vdev . name , v4l2_dev - > name , sizeof ( dev - > vdev . name ) ) ;
dev - > vdev . v4l2_dev = v4l2_dev ;
dev - > vdev . fops = & maestro_fops ;
dev - > vdev . ioctl_ops = & maestro_ioctl_ops ;
dev - > vdev . release = video_device_release_empty ;
video_set_drvdata ( & dev - > vdev , dev ) ;
retval = video_register_device ( & dev - > vdev , VFL_TYPE_RADIO , radio_nr ) ;
2006-01-10 07:52:47 +03:00
if ( retval ) {
2009-03-06 19:51:08 +03:00
v4l2_err ( v4l2_dev , " can't register video device! \n " ) ;
2006-01-10 07:52:47 +03:00
goto errfr1 ;
}
2009-03-06 19:51:08 +03:00
if ( ! radio_power_on ( dev ) ) {
2006-01-10 07:52:47 +03:00
retval = - EIO ;
goto errunr ;
}
2009-03-06 19:51:08 +03:00
v4l2_info ( v4l2_dev , " version " DRIVER_VERSION " \n " ) ;
2006-01-10 07:52:47 +03:00
return 0 ;
errunr :
2009-03-06 19:51:08 +03:00
video_unregister_device ( & dev - > vdev ) ;
2006-01-10 07:52:47 +03:00
errfr1 :
2009-03-06 19:51:08 +03:00
v4l2_device_unregister ( v4l2_dev ) ;
2006-01-10 07:52:47 +03:00
errfr :
2009-03-06 19:51:08 +03:00
kfree ( dev ) ;
2006-01-10 07:52:47 +03:00
err :
return retval ;
}
static void __devexit maestro_remove ( struct pci_dev * pdev )
{
2009-03-06 19:51:08 +03:00
struct v4l2_device * v4l2_dev = dev_get_drvdata ( & pdev - > dev ) ;
struct maestro * dev = to_maestro ( v4l2_dev ) ;
2006-01-10 07:52:47 +03:00
2009-03-06 19:51:08 +03:00
video_unregister_device ( & dev - > vdev ) ;
v4l2_device_unregister ( & dev - > v4l2_dev ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:51:08 +03:00
static struct pci_device_id maestro_r_pci_tbl [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_ESS , PCI_DEVICE_ID_ESS_ESS1968 ) ,
. class = PCI_CLASS_MULTIMEDIA_AUDIO < < 8 ,
. class_mask = 0xffff00 } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ESS , PCI_DEVICE_ID_ESS_ESS1978 ) ,
. class = PCI_CLASS_MULTIMEDIA_AUDIO < < 8 ,
. class_mask = 0xffff00 } ,
{ 0 }
} ;
MODULE_DEVICE_TABLE ( pci , maestro_r_pci_tbl ) ;
static struct pci_driver maestro_r_driver = {
. name = " maestro_radio " ,
. id_table = maestro_r_pci_tbl ,
. probe = maestro_probe ,
. remove = __devexit_p ( maestro_remove ) ,
} ;
2006-01-10 07:52:47 +03:00
static int __init maestro_radio_init ( void )
{
int retval = pci_register_driver ( & maestro_r_driver ) ;
if ( retval )
printk ( KERN_ERR " error during registration pci driver \n " ) ;
return retval ;
}
static void __exit maestro_radio_exit ( void )
{
pci_unregister_driver ( & maestro_r_driver ) ;
}
module_init ( maestro_radio_init ) ;
module_exit ( maestro_radio_exit ) ;