diff options
Diffstat (limited to 'sound/isa/wavefront/wavefront_fx.c')
-rw-r--r-- | sound/isa/wavefront/wavefront_fx.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c new file mode 100644 index 0000000..dfc449a --- /dev/null +++ b/sound/isa/wavefront/wavefront_fx.c @@ -0,0 +1,303 @@ +/* + * 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> +#include <linux/firmware.h> +#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 */ + +#define WAIT_IDLE 0xff + +#ifdef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL +#include "yss225.c" +static const struct firmware yss225_registers_firmware = { + .data = (u8 *)yss225_registers, + .size = sizeof yss225_registers +}; +#endif + +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 +snd_wavefront_fx_open (struct snd_hwdep *hw, struct file *file) + +{ + if (!try_module_get(hw->card->module)) + return -EFAULT; + file->private_data = hw; + return 0; +} + +int +snd_wavefront_fx_release (struct snd_hwdep *hw, struct file *file) + +{ + module_put(hw->card->module); + return 0; +} + +int +snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + struct snd_card *card; + 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; + if (snd_BUG_ON(!card)) + return -ENODEV; + if (snd_BUG_ON(!card->private_data)) + return -ENODEV; + + 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. + + This is the approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that outputs it. +*/ + +int __devinit +snd_wavefront_fx_start (snd_wavefront_t *dev) +{ + unsigned int i; + int err; + const struct firmware *firmware = NULL; + + if (dev->fx_initialized) + return 0; + +#ifdef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL + firmware = &yss225_registers_firmware; +#else + err = request_firmware(&firmware, "yamaha/yss225_registers.bin", + dev->card->dev); + if (err < 0) { + err = -1; + goto out; + } +#endif + + 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; + } + } else { + snd_printk(KERN_ERR "invalid address" + " in register data\n"); + err = -1; + goto out; + } + } + + dev->fx_initialized = 1; + err = 0; + +out: +#ifndef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL + release_firmware(firmware); +#endif + return err; +} + +#ifndef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL +MODULE_FIRMWARE("yamaha/yss225_registers.bin"); +#endif |