linux/sound/isa/wavefront/wavefront_fx.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) 1998-2002 by Paul Davis <pbd@op.net>
   3 *
   4 *  This program is free software; you can redistribute it and/or modify
   5 *  it under the terms of the GNU General Public License as published by
   6 *  the Free Software Foundation; either version 2 of the License, or
   7 *  (at your option) any later version.
   8 *
   9 *  This program is distributed in the hope that it will be useful,
  10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 *  GNU General Public License for more details.
  13 *
  14 *  You should have received a copy of the GNU General Public License
  15 *  along with this program; if not, write to the Free Software
  16 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  17 */
  18
  19#include <asm/io.h>
  20#include <linux/init.h>
  21#include <linux/time.h>
  22#include <linux/wait.h>
  23#include <linux/firmware.h>
  24#include <sound/core.h>
  25#include <sound/snd_wavefront.h>
  26#include <sound/initval.h>
  27
  28/* Control bits for the Load Control Register
  29 */
  30
  31#define FX_LSB_TRANSFER 0x01    /* transfer after DSP LSB byte written */
  32#define FX_MSB_TRANSFER 0x02    /* transfer after DSP MSB byte written */
  33#define FX_AUTO_INCR    0x04    /* auto-increment DSP address after transfer */
  34
  35#define WAIT_IDLE       0xff
  36
  37static int
  38wavefront_fx_idle (snd_wavefront_t *dev)
  39
  40{
  41        int i;
  42        unsigned int x = 0x80;
  43
  44        for (i = 0; i < 1000; i++) {
  45                x = inb (dev->fx_status);
  46                if ((x & 0x80) == 0) {
  47                        break;
  48                }
  49        }
  50
  51        if (x & 0x80) {
  52                snd_printk ("FX device never idle.\n");
  53                return 0;
  54        }
  55
  56        return (1);
  57}
  58
  59static void
  60wavefront_fx_mute (snd_wavefront_t *dev, int onoff)
  61
  62{
  63        if (!wavefront_fx_idle(dev)) {
  64                return;
  65        }
  66
  67        outb (onoff ? 0x02 : 0x00, dev->fx_op);
  68}
  69
  70static int
  71wavefront_fx_memset (snd_wavefront_t *dev,
  72                     int page,
  73                     int addr,
  74                     int cnt,
  75                     unsigned short *data)
  76{
  77        if (page < 0 || page > 7) {
  78                snd_printk ("FX memset: "
  79                        "page must be >= 0 and <= 7\n");
  80                return -(EINVAL);
  81        }
  82
  83        if (addr < 0 || addr > 0x7f) {
  84                snd_printk ("FX memset: "
  85                        "addr must be >= 0 and <= 7f\n");
  86                return -(EINVAL);
  87        }
  88
  89        if (cnt == 1) {
  90
  91                outb (FX_LSB_TRANSFER, dev->fx_lcr);
  92                outb (page, dev->fx_dsp_page);
  93                outb (addr, dev->fx_dsp_addr);
  94                outb ((data[0] >> 8), dev->fx_dsp_msb);
  95                outb ((data[0] & 0xff), dev->fx_dsp_lsb);
  96
  97                snd_printk ("FX: addr %d:%x set to 0x%x\n",
  98                        page, addr, data[0]);
  99
 100        } else {
 101                int i;
 102
 103                outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
 104                outb (page, dev->fx_dsp_page);
 105                outb (addr, dev->fx_dsp_addr);
 106
 107                for (i = 0; i < cnt; i++) {
 108                        outb ((data[i] >> 8), dev->fx_dsp_msb);
 109                        outb ((data[i] & 0xff), dev->fx_dsp_lsb);
 110                        if (!wavefront_fx_idle (dev)) {
 111                                break;
 112                        }
 113                }
 114
 115                if (i != cnt) {
 116                        snd_printk ("FX memset "
 117                                    "(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
 118                                    page, addr, (unsigned long) data, cnt);
 119                        return -(EIO);
 120                }
 121        }
 122
 123        return 0;
 124}
 125
 126int
 127snd_wavefront_fx_detect (snd_wavefront_t *dev)
 128
 129{
 130        /* This is a crude check, but its the best one I have for now.
 131           Certainly on the Maui and the Tropez, wavefront_fx_idle() will
 132           report "never idle", which suggests that this test should
 133           work OK.
 134        */
 135
 136        if (inb (dev->fx_status) & 0x80) {
 137                snd_printk ("Hmm, probably a Maui or Tropez.\n");
 138                return -1;
 139        }
 140
 141        return 0;
 142}
 143
 144int
 145snd_wavefront_fx_open (struct snd_hwdep *hw, struct file *file)
 146
 147{
 148        if (!try_module_get(hw->card->module))
 149                return -EFAULT;
 150        file->private_data = hw;
 151        return 0;
 152}
 153
 154int 
 155snd_wavefront_fx_release (struct snd_hwdep *hw, struct file *file)
 156
 157{
 158        module_put(hw->card->module);
 159        return 0;
 160}
 161
 162int
 163snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
 164                        unsigned int cmd, unsigned long arg)
 165
 166{
 167        struct snd_card *card;
 168        snd_wavefront_card_t *acard;
 169        snd_wavefront_t *dev;
 170        wavefront_fx_info r;
 171        unsigned short *page_data = NULL;
 172        unsigned short *pd;
 173        int err = 0;
 174
 175        card = sdev->card;
 176        if (snd_BUG_ON(!card))
 177                return -ENODEV;
 178        if (snd_BUG_ON(!card->private_data))
 179                return -ENODEV;
 180
 181        acard = card->private_data;
 182        dev = &acard->wavefront;
 183
 184        if (copy_from_user (&r, (void __user *)arg, sizeof (wavefront_fx_info)))
 185                return -EFAULT;
 186
 187        switch (r.request) {
 188        case WFFX_MUTE:
 189                wavefront_fx_mute (dev, r.data[0]);
 190                return -EIO;
 191
 192        case WFFX_MEMSET:
 193                if (r.data[2] <= 0) {
 194                        snd_printk ("cannot write "
 195                                "<= 0 bytes to FX\n");
 196                        return -EIO;
 197                } else if (r.data[2] == 1) {
 198                        pd = (unsigned short *) &r.data[3];
 199                } else {
 200                        if (r.data[2] > 256) {
 201                                snd_printk ("cannot write "
 202                                            "> 512 bytes to FX\n");
 203                                return -EIO;
 204                        }
 205                        page_data = memdup_user((unsigned char __user *)
 206                                                r.data[3],
 207                                                r.data[2] * sizeof(short));
 208                        if (IS_ERR(page_data))
 209                                return PTR_ERR(page_data);
 210                        pd = page_data;
 211                }
 212
 213                err = wavefront_fx_memset (dev,
 214                             r.data[0], /* page */
 215                             r.data[1], /* addr */
 216                             r.data[2], /* cnt */
 217                             pd);
 218                kfree(page_data);
 219                break;
 220
 221        default:
 222                snd_printk ("FX: ioctl %d not yet supported\n",
 223                            r.request);
 224                return -ENOTTY;
 225        }
 226        return err;
 227}
 228
 229/* YSS225 initialization.
 230
 231   This code was developed using DOSEMU. The Turtle Beach SETUPSND
 232   utility was run with I/O tracing in DOSEMU enabled, and a reconstruction
 233   of the port I/O done, using the Yamaha faxback document as a guide
 234   to add more logic to the code. Its really pretty weird.
 235
 236   This is the approach of just dumping the whole I/O
 237   sequence as a series of port/value pairs and a simple loop
 238   that outputs it.
 239*/
 240
 241int __devinit
 242snd_wavefront_fx_start (snd_wavefront_t *dev)
 243{
 244        unsigned int i;
 245        int err;
 246        const struct firmware *firmware = NULL;
 247
 248        if (dev->fx_initialized)
 249                return 0;
 250
 251        err = request_firmware(&firmware, "yamaha/yss225_registers.bin",
 252                               dev->card->dev);
 253        if (err < 0) {
 254                err = -1;
 255                goto out;
 256        }
 257
 258        for (i = 0; i + 1 < firmware->size; i += 2) {
 259                if (firmware->data[i] >= 8 && firmware->data[i] < 16) {
 260                        outb(firmware->data[i + 1],
 261                             dev->base + firmware->data[i]);
 262                } else if (firmware->data[i] == WAIT_IDLE) {
 263                        if (!wavefront_fx_idle(dev)) {
 264                                err = -1;
 265                                goto out;
 266                        }
 267                } else {
 268                        snd_printk(KERN_ERR "invalid address"
 269                                   " in register data\n");
 270                        err = -1;
 271                        goto out;
 272                }
 273        }
 274
 275        dev->fx_initialized = 1;
 276        err = 0;
 277
 278out:
 279        release_firmware(firmware);
 280        return err;
 281}
 282
 283MODULE_FIRMWARE("yamaha/yss225_registers.bin");
 284