2005-04-16 15:20:36 -07:00
/*
* Copyright ( c ) 1998 - 2002 by Paul Davis < pbd @ op . net >
*
* 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
*/
# include <asm/io.h>
# include <linux/init.h>
# include <linux/time.h>
# include <linux/wait.h>
2006-11-06 09:21:58 +01:00
# include <linux/firmware.h>
2005-04-16 15:20:36 -07:00
# include <sound/core.h>
# include <sound/snd_wavefront.h>
# include <sound/initval.h>
/* Control bits for the Load Control Register
*/
# define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */
# define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */
# define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */
2006-11-06 09:20:04 +01:00
# define WAIT_IDLE 0xff
2005-04-16 15:20:36 -07:00
static int
wavefront_fx_idle ( snd_wavefront_t * dev )
{
int i ;
unsigned int x = 0x80 ;
for ( i = 0 ; i < 1000 ; i + + ) {
x = inb ( dev - > fx_status ) ;
if ( ( x & 0x80 ) = = 0 ) {
break ;
}
}
if ( x & 0x80 ) {
snd_printk ( " FX device never idle. \n " ) ;
return 0 ;
}
return ( 1 ) ;
}
static void
wavefront_fx_mute ( snd_wavefront_t * dev , int onoff )
{
if ( ! wavefront_fx_idle ( dev ) ) {
return ;
}
outb ( onoff ? 0x02 : 0x00 , dev - > fx_op ) ;
}
static int
wavefront_fx_memset ( snd_wavefront_t * dev ,
int page ,
int addr ,
int cnt ,
unsigned short * data )
{
if ( page < 0 | | page > 7 ) {
snd_printk ( " FX memset: "
" page must be >= 0 and <= 7 \n " ) ;
return - ( EINVAL ) ;
}
if ( addr < 0 | | addr > 0x7f ) {
snd_printk ( " FX memset: "
" addr must be >= 0 and <= 7f \n " ) ;
return - ( EINVAL ) ;
}
if ( cnt = = 1 ) {
outb ( FX_LSB_TRANSFER , dev - > fx_lcr ) ;
outb ( page , dev - > fx_dsp_page ) ;
outb ( addr , dev - > fx_dsp_addr ) ;
outb ( ( data [ 0 ] > > 8 ) , dev - > fx_dsp_msb ) ;
outb ( ( data [ 0 ] & 0xff ) , dev - > fx_dsp_lsb ) ;
snd_printk ( " FX: addr %d:%x set to 0x%x \n " ,
page , addr , data [ 0 ] ) ;
} else {
int i ;
outb ( FX_AUTO_INCR | FX_LSB_TRANSFER , dev - > fx_lcr ) ;
outb ( page , dev - > fx_dsp_page ) ;
outb ( addr , dev - > fx_dsp_addr ) ;
for ( i = 0 ; i < cnt ; i + + ) {
outb ( ( data [ i ] > > 8 ) , dev - > fx_dsp_msb ) ;
outb ( ( data [ i ] & 0xff ) , dev - > fx_dsp_lsb ) ;
if ( ! wavefront_fx_idle ( dev ) ) {
break ;
}
}
if ( i ! = cnt ) {
snd_printk ( " FX memset "
" (0x%x, 0x%x, 0x%lx, %d) incomplete \n " ,
page , addr , ( unsigned long ) data , cnt ) ;
return - ( EIO ) ;
}
}
return 0 ;
}
int
snd_wavefront_fx_detect ( snd_wavefront_t * dev )
{
/* This is a crude check, but its the best one I have for now.
Certainly on the Maui and the Tropez , wavefront_fx_idle ( ) will
report " never idle " , which suggests that this test should
work OK .
*/
if ( inb ( dev - > fx_status ) & 0x80 ) {
snd_printk ( " Hmm, probably a Maui or Tropez. \n " ) ;
return - 1 ;
}
return 0 ;
}
int
2005-11-17 14:39:06 +01:00
snd_wavefront_fx_open ( struct snd_hwdep * hw , struct file * file )
2005-04-16 15:20:36 -07:00
{
if ( ! try_module_get ( hw - > card - > module ) )
return - EFAULT ;
file - > private_data = hw ;
return 0 ;
}
int
2005-11-17 14:39:06 +01:00
snd_wavefront_fx_release ( struct snd_hwdep * hw , struct file * file )
2005-04-16 15:20:36 -07:00
{
module_put ( hw - > card - > module ) ;
return 0 ;
}
int
2005-11-17 14:39:06 +01:00
snd_wavefront_fx_ioctl ( struct snd_hwdep * sdev , struct file * file ,
2005-04-16 15:20:36 -07:00
unsigned int cmd , unsigned long arg )
{
2005-11-17 14:39:06 +01:00
struct snd_card * card ;
2005-04-16 15:20:36 -07:00
snd_wavefront_card_t * acard ;
snd_wavefront_t * dev ;
wavefront_fx_info r ;
unsigned short * page_data = NULL ;
unsigned short * pd ;
int err = 0 ;
card = sdev - > card ;
2008-08-08 17:11:45 +02:00
if ( snd_BUG_ON ( ! card ) )
return - ENODEV ;
if ( snd_BUG_ON ( ! card - > private_data ) )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
acard = card - > private_data ;
dev = & acard - > wavefront ;
if ( copy_from_user ( & r , ( void __user * ) arg , sizeof ( wavefront_fx_info ) ) )
return - EFAULT ;
switch ( r . request ) {
case WFFX_MUTE :
wavefront_fx_mute ( dev , r . data [ 0 ] ) ;
return - EIO ;
case WFFX_MEMSET :
if ( r . data [ 2 ] < = 0 ) {
snd_printk ( " cannot write "
" <= 0 bytes to FX \n " ) ;
return - EIO ;
} else if ( r . data [ 2 ] = = 1 ) {
pd = ( unsigned short * ) & r . data [ 3 ] ;
} else {
if ( r . data [ 2 ] > 256 ) {
snd_printk ( " cannot write "
" > 512 bytes to FX \n " ) ;
return - EIO ;
}
page_data = kmalloc ( r . data [ 2 ] * sizeof ( short ) , GFP_KERNEL ) ;
if ( ! page_data )
return - ENOMEM ;
if ( copy_from_user ( page_data ,
( unsigned char __user * ) r . data [ 3 ] ,
r . data [ 2 ] * sizeof ( short ) ) ) {
kfree ( page_data ) ;
return - EFAULT ;
}
pd = page_data ;
}
err = wavefront_fx_memset ( dev ,
r . data [ 0 ] , /* page */
r . data [ 1 ] , /* addr */
r . data [ 2 ] , /* cnt */
pd ) ;
kfree ( page_data ) ;
break ;
default :
snd_printk ( " FX: ioctl %d not yet supported \n " ,
r . request ) ;
return - ENOTTY ;
}
return err ;
}
/* YSS225 initialization.
This code was developed using DOSEMU . The Turtle Beach SETUPSND
utility was run with I / O tracing in DOSEMU enabled , and a reconstruction
of the port I / O done , using the Yamaha faxback document as a guide
to add more logic to the code . Its really pretty weird .
2006-11-06 09:20:04 +01:00
This is the approach of just dumping the whole I / O
2005-04-16 15:20:36 -07:00
sequence as a series of port / value pairs and a simple loop
2006-11-06 09:20:04 +01:00
that outputs it .
2005-04-16 15:20:36 -07:00
*/
2006-07-03 16:38:28 +02:00
int __devinit
2005-04-16 15:20:36 -07:00
snd_wavefront_fx_start ( snd_wavefront_t * dev )
{
2006-11-06 09:20:04 +01:00
unsigned int i ;
2006-11-06 09:21:58 +01:00
int err ;
2007-04-26 14:13:44 +02:00
const struct firmware * firmware = NULL ;
2005-04-16 15:20:36 -07:00
2006-11-06 09:20:04 +01:00
if ( dev - > fx_initialized )
return 0 ;
2005-04-16 15:20:36 -07:00
2006-11-06 09:21:58 +01:00
err = request_firmware ( & firmware , " yamaha/yss225_registers.bin " ,
dev - > card - > dev ) ;
if ( err < 0 ) {
err = - 1 ;
goto out ;
}
for ( i = 0 ; i + 1 < firmware - > size ; i + = 2 ) {
if ( firmware - > data [ i ] > = 8 & & firmware - > data [ i ] < 16 ) {
outb ( firmware - > data [ i + 1 ] ,
dev - > base + firmware - > data [ i ] ) ;
} else if ( firmware - > data [ i ] = = WAIT_IDLE ) {
if ( ! wavefront_fx_idle ( dev ) ) {
err = - 1 ;
goto out ;
}
2006-11-06 09:20:04 +01:00
} else {
snd_printk ( KERN_ERR " invalid address "
" in register data \n " ) ;
2006-11-06 09:21:58 +01:00
err = - 1 ;
goto out ;
2005-04-16 15:20:36 -07:00
}
}
2006-11-06 09:20:04 +01:00
dev - > fx_initialized = 1 ;
2006-11-06 09:21:58 +01:00
err = 0 ;
out :
2007-04-26 14:13:44 +02:00
release_firmware ( firmware ) ;
2006-11-06 09:21:58 +01:00
return err ;
2005-04-16 15:20:36 -07:00
}
2007-05-03 17:59:54 +02:00
MODULE_FIRMWARE ( " yamaha/yss225_registers.bin " ) ;