diff options
Diffstat (limited to 'sound/oss/waveartist.c')
-rw-r--r-- | sound/oss/waveartist.c | 2043 |
1 files changed, 0 insertions, 2043 deletions
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c deleted file mode 100644 index 4f0c3a232e41..000000000000 --- a/sound/oss/waveartist.c +++ /dev/null @@ -1,2043 +0,0 @@ -/* - * linux/sound/oss/waveartist.c - * - * The low level driver for the RWA010 Rockwell Wave Artist - * codec chip used in the Rebel.com NetWinder. - * - * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk) - * and Pat Beirne (patb@corel.ca) - * - * - * Copyright (C) by Rebel.com 1998-1999 - * - * RWA010 specs received under NDA from Rockwell - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> - * Added __init to waveartist_init() - */ - -/* Debugging */ -#define DEBUG_CMD 1 -#define DEBUG_OUT 2 -#define DEBUG_IN 4 -#define DEBUG_INTR 8 -#define DEBUG_MIXER 16 -#define DEBUG_TRIGGER 32 - -#define debug_flg (0) - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/bitops.h> - - -#include "sound_config.h" -#include "waveartist.h" - -#ifdef CONFIG_ARM -#include <mach/hardware.h> -#include <asm/mach-types.h> -#endif - -#ifndef NO_DMA -#define NO_DMA 255 -#endif - -#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ - SOUND_MASK_PCM |\ - SOUND_MASK_LINE |\ - SOUND_MASK_MIC |\ - SOUND_MASK_LINE1 |\ - SOUND_MASK_RECLEV |\ - SOUND_MASK_VOLUME |\ - SOUND_MASK_IMIX) - -static unsigned short levels[SOUND_MIXER_NRDEVICES] = { - 0x5555, /* Master Volume */ - 0x0000, /* Bass */ - 0x0000, /* Treble */ - 0x2323, /* Synth (FM) */ - 0x4b4b, /* PCM */ - 0x6464, /* PC Speaker */ - 0x0000, /* Ext Line */ - 0x0000, /* Mic */ - 0x0000, /* CD */ - 0x6464, /* Recording monitor */ - 0x0000, /* SB PCM (ALT PCM) */ - 0x0000, /* Recording level */ - 0x6464, /* Input gain */ - 0x6464, /* Output gain */ - 0x0000, /* Line1 (Aux1) */ - 0x0000, /* Line2 (Aux2) */ - 0x0000, /* Line3 (Aux3) */ - 0x0000, /* Digital1 */ - 0x0000, /* Digital2 */ - 0x0000, /* Digital3 */ - 0x0000, /* Phone In */ - 0x6464, /* Phone Out */ - 0x0000, /* Video */ - 0x0000, /* Radio */ - 0x0000 /* Monitor */ -}; - -struct wavnc_info { - struct address_info hw; /* hardware */ - char *chip_name; - - int xfer_count; - int audio_mode; - int open_mode; - int audio_flags; - int record_dev; - int playback_dev; - int dev_no; - - /* Mixer parameters */ - const struct waveartist_mixer_info *mix; - - unsigned short *levels; /* cache of volume settings */ - int recmask; /* currently enabled recording device! */ - -#ifdef CONFIG_ARCH_NETWINDER - signed int slider_vol; /* hardware slider volume */ - unsigned int handset_detect :1; - unsigned int telephone_detect:1; - unsigned int no_autoselect :1;/* handset/telephone autoselects a path */ - unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */ - unsigned int line_mute_state :1;/* set by ioctl or autoselect */ - unsigned int use_slider :1;/* use slider setting for o/p vol */ -#endif -}; - -/* - * This is the implementation specific mixer information. - */ -struct waveartist_mixer_info { - unsigned int supported_devs; /* Supported devices */ - unsigned int recording_devs; /* Recordable devies */ - unsigned int stereo_devs; /* Stereo devices */ - - unsigned int (*select_input)(struct wavnc_info *, unsigned int, - unsigned char *, unsigned char *); - int (*decode_mixer)(struct wavnc_info *, int, - unsigned char, unsigned char); - int (*get_mixer)(struct wavnc_info *, int); -}; - -struct wavnc_port_info { - int open_mode; - int speed; - int channels; - int audio_format; -}; - -static int nr_waveartist_devs; -static struct wavnc_info adev_info[MAX_AUDIO_DEV]; -static DEFINE_SPINLOCK(waveartist_lock); - -#ifndef CONFIG_ARCH_NETWINDER -#define machine_is_netwinder() 0 -#else -static struct timer_list vnc_timer; -static void vnc_configure_mixer(struct wavnc_info *devc, - unsigned int input_mask); -static int vnc_private_ioctl(int dev, unsigned int cmd, int __user *arg); -static void vnc_slider_tick(unsigned long data); -#endif - -static inline void -waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) -{ - unsigned int ctlr_port = hw->io_base + CTLR; - - clear = ~clear & inb(ctlr_port); - - outb(clear | set, ctlr_port); -} - -/* Toggle IRQ acknowledge line - */ -static inline void -waveartist_iack(struct wavnc_info *devc) -{ - unsigned int ctlr_port = devc->hw.io_base + CTLR; - int old_ctlr; - - old_ctlr = inb(ctlr_port) & ~IRQ_ACK; - - outb(old_ctlr | IRQ_ACK, ctlr_port); - outb(old_ctlr, ctlr_port); -} - -static inline int -waveartist_sleep(int timeout_ms) -{ - unsigned int timeout = msecs_to_jiffies(timeout_ms*100); - return schedule_timeout_interruptible(timeout); -} - -static int -waveartist_reset(struct wavnc_info *devc) -{ - struct address_info *hw = &devc->hw; - unsigned int timeout, res = -1; - - waveartist_set_ctlr(hw, -1, RESET); - waveartist_sleep(2); - waveartist_set_ctlr(hw, RESET, 0); - - timeout = 500; - do { - mdelay(2); - - if (inb(hw->io_base + STATR) & CMD_RF) { - res = inw(hw->io_base + CMDR); - if (res == 0x55aa) - break; - } - } while (--timeout); - - if (timeout == 0) { - printk(KERN_WARNING "WaveArtist: reset timeout "); - if (res != (unsigned int)-1) - printk("(res=%04X)", res); - printk("\n"); - return 1; - } - return 0; -} - -/* Helper function to send and receive words - * from WaveArtist. It handles all the handshaking - * and can send or receive multiple words. - */ -static int -waveartist_cmd(struct wavnc_info *devc, - int nr_cmd, unsigned int *cmd, - int nr_resp, unsigned int *resp) -{ - unsigned int io_base = devc->hw.io_base; - unsigned int timed_out = 0; - unsigned int i; - - if (debug_flg & DEBUG_CMD) { - printk("waveartist_cmd: cmd="); - - for (i = 0; i < nr_cmd; i++) - printk("%04X ", cmd[i]); - - printk("\n"); - } - - if (inb(io_base + STATR) & CMD_RF) { - int old_data; - - /* flush the port - */ - - old_data = inw(io_base + CMDR); - - if (debug_flg & DEBUG_CMD) - printk("flushed %04X...", old_data); - - udelay(10); - } - - for (i = 0; !timed_out && i < nr_cmd; i++) { - int count; - - for (count = 5000; count; count--) - if (inb(io_base + STATR) & CMD_WE) - break; - - if (!count) - timed_out = 1; - else - outw(cmd[i], io_base + CMDR); - } - - for (i = 0; !timed_out && i < nr_resp; i++) { - int count; - - for (count = 5000; count; count--) - if (inb(io_base + STATR) & CMD_RF) - break; - - if (!count) - timed_out = 1; - else - resp[i] = inw(io_base + CMDR); - } - - if (debug_flg & DEBUG_CMD) { - if (!timed_out) { - printk("waveartist_cmd: resp="); - - for (i = 0; i < nr_resp; i++) - printk("%04X ", resp[i]); - - printk("\n"); - } else - printk("waveartist_cmd: timed out\n"); - } - - return timed_out ? 1 : 0; -} - -/* - * Send one command word - */ -static inline int -waveartist_cmd1(struct wavnc_info *devc, unsigned int cmd) -{ - return waveartist_cmd(devc, 1, &cmd, 0, NULL); -} - -/* - * Send one command, receive one word - */ -static inline unsigned int -waveartist_cmd1_r(struct wavnc_info *devc, unsigned int cmd) -{ - unsigned int ret; - - waveartist_cmd(devc, 1, &cmd, 1, &ret); - - return ret; -} - -/* - * Send a double command, receive one - * word (and throw it away) - */ -static inline int -waveartist_cmd2(struct wavnc_info *devc, unsigned int cmd, unsigned int arg) -{ - unsigned int vals[2]; - - vals[0] = cmd; - vals[1] = arg; - - return waveartist_cmd(devc, 2, vals, 1, vals); -} - -/* - * Send a triple command - */ -static inline int -waveartist_cmd3(struct wavnc_info *devc, unsigned int cmd, - unsigned int arg1, unsigned int arg2) -{ - unsigned int vals[3]; - - vals[0] = cmd; - vals[1] = arg1; - vals[2] = arg2; - - return waveartist_cmd(devc, 3, vals, 0, NULL); -} - -static int -waveartist_getrev(struct wavnc_info *devc, char *rev) -{ - unsigned int temp[2]; - unsigned int cmd = WACMD_GETREV; - - waveartist_cmd(devc, 1, &cmd, 2, temp); - - rev[0] = temp[0] >> 8; - rev[1] = temp[0] & 255; - rev[2] = '\0'; - - return temp[0]; -} - -static void waveartist_halt_output(int dev); -static void waveartist_halt_input(int dev); -static void waveartist_halt(int dev); -static void waveartist_trigger(int dev, int state); - -static int -waveartist_open(int dev, int mode) -{ - struct wavnc_info *devc; - struct wavnc_port_info *portc; - unsigned long flags; - - if (dev < 0 || dev >= num_audiodevs) - return -ENXIO; - - devc = (struct wavnc_info *) audio_devs[dev]->devc; - portc = (struct wavnc_port_info *) audio_devs[dev]->portc; - - spin_lock_irqsave(&waveartist_lock, flags); - if (portc->open_mode || (devc->open_mode & mode)) { - spin_unlock_irqrestore(&waveartist_lock, flags); - return -EBUSY; - } - - devc->audio_mode = 0; - devc->open_mode |= mode; - portc->open_mode = mode; - waveartist_trigger(dev, 0); - - if (mode & OPEN_READ) - devc->record_dev = dev; - if (mode & OPEN_WRITE) - devc->playback_dev = dev; - spin_unlock_irqrestore(&waveartist_lock, flags); - - return 0; -} - -static void -waveartist_close(int dev) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - waveartist_halt(dev); - - devc->audio_mode = 0; - devc->open_mode &= ~portc->open_mode; - portc->open_mode = 0; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - unsigned int count = __count; - - if (debug_flg & DEBUG_OUT) - printk("waveartist: output block, buf=0x%lx, count=0x%x...\n", - buf, count); - /* - * 16 bit data - */ - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) - count >>= 1; - - if (portc->channels > 1) - count >>= 1; - - count -= 1; - - if (devc->audio_mode & PCM_ENABLE_OUTPUT && - audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - count == devc->xfer_count) { - devc->audio_mode |= PCM_ENABLE_OUTPUT; - return; /* - * Auto DMA mode on. No need to react - */ - } - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * set sample count - */ - waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count); - - devc->xfer_count = count; - devc->audio_mode |= PCM_ENABLE_OUTPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - unsigned int count = __count; - - if (debug_flg & DEBUG_IN) - printk("waveartist: start input, buf=0x%lx, count=0x%x...\n", - buf, count); - - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - count >>= 1; - - if (portc->channels > 1) - count >>= 1; - - count -= 1; - - if (devc->audio_mode & PCM_ENABLE_INPUT && - audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - count == devc->xfer_count) { - devc->audio_mode |= PCM_ENABLE_INPUT; - return; /* - * Auto DMA mode on. No need to react - */ - } - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * set sample count - */ - waveartist_cmd2(devc, WACMD_INPUTSIZE, count); - - devc->xfer_count = count; - devc->audio_mode |= PCM_ENABLE_INPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static int -waveartist_ioctl(int dev, unsigned int cmd, void __user * arg) -{ - return -EINVAL; -} - -static unsigned int -waveartist_get_speed(struct wavnc_port_info *portc) -{ - unsigned int speed; - - /* - * program the speed, channels, bits - */ - if (portc->speed == 8000) - speed = 0x2E71; - else if (portc->speed == 11025) - speed = 0x4000; - else if (portc->speed == 22050) - speed = 0x8000; - else if (portc->speed == 44100) - speed = 0x0; - else { - /* - * non-standard - just calculate - */ - speed = portc->speed << 16; - - speed = (speed / 44100) & 65535; - } - - return speed; -} - -static unsigned int -waveartist_get_bits(struct wavnc_port_info *portc) -{ - unsigned int bits; - - if (portc->audio_format == AFMT_S16_LE) - bits = 1; - else if (portc->audio_format == AFMT_S8) - bits = 0; - else - bits = 2; //default AFMT_U8 - - return bits; -} - -static int -waveartist_prepare_for_input(int dev, int bsize, int bcount) -{ - unsigned long flags; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned int speed, bits; - - if (devc->audio_mode) - return 0; - - speed = waveartist_get_speed(portc); - bits = waveartist_get_bits(portc); - - spin_lock_irqsave(&waveartist_lock, flags); - - if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the " - "record format to %d\n", portc->audio_format); - - if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels)) - printk(KERN_WARNING "waveartist: error setting record " - "to %d channels\n", portc->channels); - - /* - * write cmd SetSampleSpeedTimeConstant - */ - if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed)) - printk(KERN_WARNING "waveartist: error setting the record " - "speed to %dHz.\n", portc->speed); - - if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1)) - printk(KERN_WARNING "waveartist: error setting the record " - "data path to 0x%X\n", 1); - - if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the record " - "format to %d\n", portc->audio_format); - - devc->xfer_count = 0; - spin_unlock_irqrestore(&waveartist_lock, flags); - waveartist_halt_input(dev); - - if (debug_flg & DEBUG_INTR) { - printk("WA CTLR reg: 0x%02X.\n", - inb(devc->hw.io_base + CTLR)); - printk("WA STAT reg: 0x%02X.\n", - inb(devc->hw.io_base + STATR)); - printk("WA IRQS reg: 0x%02X.\n", - inb(devc->hw.io_base + IRQSTAT)); - } - - return 0; -} - -static int -waveartist_prepare_for_output(int dev, int bsize, int bcount) -{ - unsigned long flags; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned int speed, bits; - - /* - * program the speed, channels, bits - */ - speed = waveartist_get_speed(portc); - bits = waveartist_get_bits(portc); - - spin_lock_irqsave(&waveartist_lock, flags); - - if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) && - waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed)) - printk(KERN_WARNING "waveartist: error setting the playback " - "speed to %dHz.\n", portc->speed); - - if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels)) - printk(KERN_WARNING "waveartist: error setting the playback " - "to %d channels\n", portc->channels); - - if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0)) - printk(KERN_WARNING "waveartist: error setting the playback " - "data path to 0x%X\n", 0); - - if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the playback " - "format to %d\n", portc->audio_format); - - devc->xfer_count = 0; - spin_unlock_irqrestore(&waveartist_lock, flags); - waveartist_halt_output(dev); - - if (debug_flg & DEBUG_INTR) { - printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR)); - printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR)); - printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT)); - } - - return 0; -} - -static void -waveartist_halt(int dev) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - struct wavnc_info *devc; - - if (portc->open_mode & OPEN_WRITE) - waveartist_halt_output(dev); - - if (portc->open_mode & OPEN_READ) - waveartist_halt_input(dev); - - devc = (struct wavnc_info *) audio_devs[dev]->devc; - devc->audio_mode = 0; -} - -static void -waveartist_halt_input(int dev) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * Stop capture - */ - waveartist_cmd1(devc, WACMD_INPUTSTOP); - - devc->audio_mode &= ~PCM_ENABLE_INPUT; - - /* - * Clear interrupt by toggling - * the IRQ_ACK bit in CTRL - */ - if (inb(devc->hw.io_base + STATR) & IRQ_REQ) - waveartist_iack(devc); - -// devc->audio_mode &= ~PCM_ENABLE_INPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_halt_output(int dev) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - waveartist_cmd1(devc, WACMD_OUTPUTSTOP); - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - /* - * Clear interrupt by toggling - * the IRQ_ACK bit in CTRL - */ - if (inb(devc->hw.io_base + STATR) & IRQ_REQ) - waveartist_iack(devc); - -// devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_trigger(int dev, int state) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned long flags; - - if (debug_flg & DEBUG_TRIGGER) { - printk("wavnc: audio trigger "); - if (state & PCM_ENABLE_INPUT) - printk("in "); - if (state & PCM_ENABLE_OUTPUT) - printk("out"); - printk("\n"); - } - - spin_lock_irqsave(&waveartist_lock, flags); - - state &= devc->audio_mode; - - if (portc->open_mode & OPEN_READ && - state & PCM_ENABLE_INPUT) - /* - * enable ADC Data Transfer to PC - */ - waveartist_cmd1(devc, WACMD_INPUTSTART); - - if (portc->open_mode & OPEN_WRITE && - state & PCM_ENABLE_OUTPUT) - /* - * enable DAC data transfer from PC - */ - waveartist_cmd1(devc, WACMD_OUTPUTSTART); - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static int -waveartist_set_speed(int dev, int arg) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - - if (arg <= 0) - return portc->speed; - - if (arg < 5000) - arg = 5000; - if (arg > 44100) - arg = 44100; - - portc->speed = arg; - return portc->speed; - -} - -static short -waveartist_set_channels(int dev, short arg) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - - if (arg != 1 && arg != 2) - return portc->channels; - - portc->channels = arg; - return arg; -} - -static unsigned int -waveartist_set_bits(int dev, unsigned int arg) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - - if (arg == 0) - return portc->audio_format; - - if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8)) - arg = AFMT_U8; - - portc->audio_format = arg; - - return arg; -} - -static struct audio_driver waveartist_audio_driver = { - .owner = THIS_MODULE, - .open = waveartist_open, - .close = waveartist_close, - .output_block = waveartist_output_block, - .start_input = waveartist_start_input, - .ioctl = waveartist_ioctl, - .prepare_for_input = waveartist_prepare_for_input, - .prepare_for_output = waveartist_prepare_for_output, - .halt_io = waveartist_halt, - .halt_input = waveartist_halt_input, - .halt_output = waveartist_halt_output, - .trigger = waveartist_trigger, - .set_speed = waveartist_set_speed, - .set_bits = waveartist_set_bits, - .set_channels = waveartist_set_channels -}; - - -static irqreturn_t -waveartist_intr(int irq, void *dev_id) -{ - struct wavnc_info *devc = dev_id; - int irqstatus, status; - - spin_lock(&waveartist_lock); - irqstatus = inb(devc->hw.io_base + IRQSTAT); - status = inb(devc->hw.io_base + STATR); - - if (debug_flg & DEBUG_INTR) - printk("waveartist_intr: stat=%02x, irqstat=%02x\n", - status, irqstatus); - - if (status & IRQ_REQ) /* Clear interrupt */ - waveartist_iack(devc); - else - printk(KERN_WARNING "waveartist: unexpected interrupt\n"); - - if (irqstatus & 0x01) { - int temp = 1; - - /* PCM buffer done - */ - if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) { - DMAbuf_outputintr(devc->playback_dev, 1); - temp = 0; - } - if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) { - DMAbuf_inputintr(devc->record_dev); - temp = 0; - } - if (temp) //default: - printk(KERN_WARNING "waveartist: Unknown interrupt\n"); - } - if (irqstatus & 0x2) - // We do not use SB mode natively... - printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n"); - spin_unlock(&waveartist_lock); - return IRQ_HANDLED; -} - -/* ------------------------------------------------------------------------- - * Mixer stuff - */ -struct mix_ent { - unsigned char reg_l; - unsigned char reg_r; - unsigned char shift; - unsigned char max; -}; - -static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = { - { 2, 6, 1, 7 }, /* SOUND_MIXER_VOLUME */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_BASS */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_TREBLE */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_SYNTH */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PCM */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_SPEAKER */ - { 0, 4, 6, 31 }, /* SOUND_MIXER_LINE */ - { 2, 6, 4, 3 }, /* SOUND_MIXER_MIC */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_CD */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_IMIX */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_ALTPCM */ -#if 0 - { 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_IGAIN */ -#else - { 0, 0, 0, 0 }, /* SOUND_MIXER_RECLEV */ - { 3, 7, 0, 7 }, /* SOUND_MIXER_IGAIN */ -#endif - { 0, 0, 0, 0 }, /* SOUND_MIXER_OGAIN */ - { 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1 */ - { 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_LINE3 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL1 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL2 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL3 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEIN */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEOUT */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_VIDEO */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_RADIO */ - { 0, 0, 0, 0 } /* SOUND_MIXER_MONITOR */ -}; - -static void -waveartist_mixer_update(struct wavnc_info *devc, int whichDev) -{ - unsigned int lev_left, lev_right; - - lev_left = devc->levels[whichDev] & 0xff; - lev_right = devc->levels[whichDev] >> 8; - - if (lev_left > 100) - lev_left = 100; - if (lev_right > 100) - lev_right = 100; - -#define SCALE(lev,max) ((lev) * (max) / 100) - - if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT) - whichDev = SOUND_MIXER_VOLUME; - - if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) { - const struct mix_ent *mix = mix_devs + whichDev; - unsigned int mask, left, right; - - mask = mix->max << mix->shift; - lev_left = SCALE(lev_left, mix->max) << mix->shift; - lev_right = SCALE(lev_right, mix->max) << mix->shift; - - /* read left setting */ - left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | - mix->reg_l << 8); - - /* read right setting */ - right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | - mix->reg_r << 8); - - left = (left & ~mask) | (lev_left & mask); - right = (right & ~mask) | (lev_right & mask); - - /* write left,right back */ - waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); - } else { - switch(whichDev) { - case SOUND_MIXER_PCM: - waveartist_cmd3(devc, WACMD_SET_LEVEL, - SCALE(lev_left, 32767), - SCALE(lev_right, 32767)); - break; - - case SOUND_MIXER_SYNTH: - waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL, - SCALE(lev_left, 32767), - SCALE(lev_right, 32767)); - break; - } - } -} - -/* - * Set the ADC MUX to the specified values. We do NOT do any - * checking of the values passed, since we assume that the - * relevant *_select_input function has done that for us. - */ -static void -waveartist_set_adc_mux(struct wavnc_info *devc, char left_dev, - char right_dev) -{ - unsigned int reg_08, reg_09; - - reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800); - reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900); - - reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev; - - waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09); -} - -/* - * Decode a recording mask into a mixer selection as follows: - * - * OSS Source WA Source Actual source - * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) - * SOUND_MASK_LINE Line Line in - * SOUND_MASK_LINE1 Aux 1 Aux 1 in - * SOUND_MASK_LINE2 Aux 2 Aux 2 in - * SOUND_MASK_MIC Mic Microphone - */ -static unsigned int -waveartist_select_input(struct wavnc_info *devc, unsigned int recmask, - unsigned char *dev_l, unsigned char *dev_r) -{ - unsigned int recdev = ADC_MUX_NONE; - - if (recmask & SOUND_MASK_IMIX) { - recmask = SOUND_MASK_IMIX; - recdev = ADC_MUX_MIXER; - } else if (recmask & SOUND_MASK_LINE2) { - recmask = SOUND_MASK_LINE2; - recdev = ADC_MUX_AUX2; - } else if (recmask & SOUND_MASK_LINE1) { - recmask = SOUND_MASK_LINE1; - recdev = ADC_MUX_AUX1; - } else if (recmask & SOUND_MASK_LINE) { - recmask = SOUND_MASK_LINE; - recdev = ADC_MUX_LINE; - } else if (recmask & SOUND_MASK_MIC) { - recmask = SOUND_MASK_MIC; - recdev = ADC_MUX_MIC; - } - - *dev_l = *dev_r = recdev; - - return recmask; -} - -static int -waveartist_decode_mixer(struct wavnc_info *devc, int dev, - unsigned char lev_l, - unsigned char lev_r) -{ - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_MIC: - case SOUND_MIXER_IGAIN: - case SOUND_MIXER_LINE1: - case SOUND_MIXER_LINE2: - devc->levels[dev] = lev_l | lev_r << 8; - break; - - case SOUND_MIXER_IMIX: - break; - - default: - dev = -EINVAL; - break; - } - - return dev; -} - -static int waveartist_get_mixer(struct wavnc_info *devc, int dev) -{ - return devc->levels[dev]; -} - -static const struct waveartist_mixer_info waveartist_mixer = { - .supported_devs = SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN, - .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | - SOUND_MASK_IMIX, - .stereo_devs = (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~ - (SOUND_MASK_SPEAKER | SOUND_MASK_IMIX), - .select_input = waveartist_select_input, - .decode_mixer = waveartist_decode_mixer, - .get_mixer = waveartist_get_mixer, -}; - -static void -waveartist_set_recmask(struct wavnc_info *devc, unsigned int recmask) -{ - unsigned char dev_l, dev_r; - - recmask &= devc->mix->recording_devs; - - /* - * If more than one recording device selected, - * disable the device that is currently in use. - */ - if (hweight32(recmask) > 1) - recmask &= ~devc->recmask; - - /* - * Translate the recording device mask into - * the ADC multiplexer settings. - */ - devc->recmask = devc->mix->select_input(devc, recmask, - &dev_l, &dev_r); - - waveartist_set_adc_mux(devc, dev_l, dev_r); -} - -static int -waveartist_set_mixer(struct wavnc_info *devc, int dev, unsigned int level) -{ - unsigned int lev_left = level & 0x00ff; - unsigned int lev_right = (level & 0xff00) >> 8; - - if (lev_left > 100) - lev_left = 100; - if (lev_right > 100) - lev_right = 100; - - /* - * Mono devices have their right volume forced to their - * left volume. (from ALSA driver OSS emulation). - */ - if (!(devc->mix->stereo_devs & (1 << dev))) - lev_right = lev_left; - - dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right); - - if (dev >= 0) - waveartist_mixer_update(devc, dev); - - return dev < 0 ? dev : 0; -} - -static int -waveartist_mixer_ioctl(int dev, unsigned int cmd, void __user * arg) -{ - struct wavnc_info *devc = (struct wavnc_info *)audio_devs[dev]->devc; - int ret = 0, val, nr; - - /* - * All SOUND_MIXER_* ioctls use type 'M' - */ - if (((cmd >> 8) & 255) != 'M') - return -ENOIOCTLCMD; - -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) { - ret = vnc_private_ioctl(dev, cmd, arg); - if (ret != -ENOIOCTLCMD) - return ret; - else - ret = 0; - } -#endif - - nr = cmd & 0xff; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - if (get_user(val, (int __user *)arg)) - return -EFAULT; - - switch (nr) { - case SOUND_MIXER_RECSRC: - waveartist_set_recmask(devc, val); - break; - - default: - ret = -EINVAL; - if (nr < SOUND_MIXER_NRDEVICES && - devc->mix->supported_devs & (1 << nr)) - ret = waveartist_set_mixer(devc, nr, val); - } - } - - if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) { - ret = -EINVAL; - - switch (nr) { - case SOUND_MIXER_RECSRC: - ret = devc->recmask; - break; - - case SOUND_MIXER_DEVMASK: - ret = devc->mix->supported_devs; - break; - - case SOUND_MIXER_STEREODEVS: - ret = devc->mix->stereo_devs; - break; - - case SOUND_MIXER_RECMASK: - ret = devc->mix->recording_devs; - break; - - case SOUND_MIXER_CAPS: - ret = SOUND_CAP_EXCL_INPUT; - break; - - default: - if (nr < SOUND_MIXER_NRDEVICES) - ret = devc->mix->get_mixer(devc, nr); - break; - } - - if (ret >= 0) - ret = put_user(ret, (int __user *)arg) ? -EFAULT : 0; - } - - return ret; -} - -static struct mixer_operations waveartist_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "WaveArtist", - .name = "WaveArtist", - .ioctl = waveartist_mixer_ioctl -}; - -static void -waveartist_mixer_reset(struct wavnc_info *devc) -{ - int i; - - if (debug_flg & DEBUG_MIXER) - printk("%s: mixer_reset\n", devc->hw.name); - - /* - * reset mixer cmd - */ - waveartist_cmd1(devc, WACMD_RST_MIXER); - - /* - * set input for ADC to come from 'quiet' - * turn on default modes - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836); - - /* - * set mixer input select to none, RX filter gains 0 dB - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00); - - /* - * set bit 0 reg 2 to 1 - unmute MonoOut - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800); - - /* set default input device = internal mic - * current recording device = none - */ - waveartist_set_recmask(devc, 0); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - waveartist_mixer_update(devc, i); -} - -static int __init waveartist_init(struct wavnc_info *devc) -{ - struct wavnc_port_info *portc; - char rev[3], dev_name[64]; - int my_dev; - - if (waveartist_reset(devc)) - return -ENODEV; - - sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name); - - if (waveartist_getrev(devc, rev)) { - strcat(dev_name, " rev. "); - strcat(dev_name, rev); - } - strcat(dev_name, ")"); - - conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq, - devc->hw.dma, devc->hw.dma2); - - portc = kzalloc(sizeof(struct wavnc_port_info), GFP_KERNEL); - if (portc == NULL) - goto nomem; - - my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name, - &waveartist_audio_driver, sizeof(struct audio_driver), - devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8, - devc, devc->hw.dma, devc->hw.dma2); - - if (my_dev < 0) - goto free; - - audio_devs[my_dev]->portc = portc; - - waveartist_mixer_reset(devc); - - /* - * clear any pending interrupt - */ - waveartist_iack(devc); - - if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) { - printk(KERN_ERR "%s: IRQ %d in use\n", - devc->hw.name, devc->hw.irq); - goto uninstall; - } - - if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) { - printk(KERN_ERR "%s: Can't allocate DMA%d\n", - devc->hw.name, devc->hw.dma); - goto uninstall_irq; - } - - if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA) - if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) { - printk(KERN_ERR "%s: can't allocate DMA%d\n", - devc->hw.name, devc->hw.dma2); - goto uninstall_dma; - } - - waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE); - - audio_devs[my_dev]->mixer_dev = - sound_install_mixer(MIXER_DRIVER_VERSION, - dev_name, - &waveartist_mixer_operations, - sizeof(struct mixer_operations), - devc); - - return my_dev; - -uninstall_dma: - sound_free_dma(devc->hw.dma); - -uninstall_irq: - free_irq(devc->hw.irq, devc); - -uninstall: - sound_unload_audiodev(my_dev); - -free: - kfree(portc); - -nomem: - return -1; -} - -static int __init probe_waveartist(struct address_info *hw_config) -{ - struct wavnc_info *devc = &adev_info[nr_waveartist_devs]; - - if (nr_waveartist_devs >= MAX_AUDIO_DEV) { - printk(KERN_WARNING "waveartist: too many audio devices\n"); - return 0; - } - - if (!request_region(hw_config->io_base, 15, hw_config->name)) { - printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); - return 0; - } - - if (hw_config->irq > 15 || hw_config->irq < 0) { - release_region(hw_config->io_base, 15); - printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", - hw_config->irq); - return 0; - } - - if (hw_config->dma != 3) { - release_region(hw_config->io_base, 15); - printk(KERN_WARNING "WaveArtist: Bad DMA %d\n", - hw_config->dma); - return 0; - } - - hw_config->name = "WaveArtist"; - devc->hw = *hw_config; - devc->open_mode = 0; - devc->chip_name = "RWA-010"; - - return 1; -} - -static void __init -attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix) -{ - struct wavnc_info *devc = &adev_info[nr_waveartist_devs]; - - /* - * NOTE! If irq < 0, there is another driver which has allocated the - * IRQ so that this driver doesn't need to allocate/deallocate it. - * The actually used IRQ is ABS(irq). - */ - devc->hw = *hw; - devc->hw.irq = (hw->irq > 0) ? hw->irq : 0; - devc->open_mode = 0; - devc->playback_dev = 0; - devc->record_dev = 0; - devc->audio_flags = DMA_AUTOMODE; - devc->levels = levels; - - if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA) - devc->audio_flags |= DMA_DUPLEX; - - devc->mix = mix; - devc->dev_no = waveartist_init(devc); - - if (devc->dev_no < 0) - release_region(hw->io_base, 15); - else { -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) { - setup_timer(&vnc_timer, vnc_slider_tick, - nr_waveartist_devs); - mod_timer(&vnc_timer, jiffies); - - vnc_configure_mixer(devc, 0); - - devc->no_autoselect = 1; - } -#endif - nr_waveartist_devs += 1; - } -} - -static void __exit unload_waveartist(struct address_info *hw) -{ - struct wavnc_info *devc = NULL; - int i; - - for (i = 0; i < nr_waveartist_devs; i++) - if (hw->io_base == adev_info[i].hw.io_base) { - devc = adev_info + i; - break; - } - - if (devc != NULL) { - int mixer; - -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) - del_timer(&vnc_timer); -#endif - - release_region(devc->hw.io_base, 15); - - waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0); - - if (devc->hw.irq >= 0) - free_irq(devc->hw.irq, devc); - - sound_free_dma(devc->hw.dma); - - if (devc->hw.dma != devc->hw.dma2 && - devc->hw.dma2 != NO_DMA) - sound_free_dma(devc->hw.dma2); - - mixer = audio_devs[devc->dev_no]->mixer_dev; - - if (mixer >= 0) - sound_unload_mixerdev(mixer); - - if (devc->dev_no >= 0) - sound_unload_audiodev(devc->dev_no); - - nr_waveartist_devs -= 1; - - for (; i < nr_waveartist_devs; i++) - adev_info[i] = adev_info[i + 1]; - } else - printk(KERN_WARNING "waveartist: can't find device " - "to unload\n"); -} - -#ifdef CONFIG_ARCH_NETWINDER - -/* - * Rebel.com Netwinder specifics... - */ - -#include <asm/hardware/dec21285.h> - -#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec - -#define MIXER_PRIVATE3_RESET 0x53570000 -#define MIXER_PRIVATE3_READ 0x53570001 -#define MIXER_PRIVATE3_WRITE 0x53570002 - -#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit -#define VNC_MUTE_LINE_OUT 0x10 -#define VNC_PHONE_DETECT 0x20 -#define VNC_HANDSET_DETECT 0x40 -#define VNC_DISABLE_AUTOSWITCH 0x80 - -static inline void -vnc_mute_spkr(struct wavnc_info *devc) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&nw_gpio_lock, flags); - nw_cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE); - raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); -} - -static void -vnc_mute_lout(struct wavnc_info *devc) -{ - unsigned int left, right; - - left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL); - right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400); - - if (devc->line_mute_state) { - left &= ~1; - right &= ~1; - } else { - left |= 1; - right |= 1; - } - waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); - -} - -static int -vnc_volume_slider(struct wavnc_info *devc) -{ - static signed int old_slider_volume; - unsigned long flags; - signed int volume = 255; - - *CSR_TIMER1_LOAD = 0x00ffffff; - - spin_lock_irqsave(&waveartist_lock, flags); - - outb(0xFF, 0x201); - *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; - - while (volume && (inb(0x201) & 0x01)) - volume--; - - *CSR_TIMER1_CNTL = 0; - - spin_unlock_irqrestore(&waveartist_lock,flags); - - volume = 0x00ffffff - *CSR_TIMER1_VALUE; - - -#ifndef REVERSE - volume = 150 - (volume >> 5); -#else - volume = (volume >> 6) - 25; -#endif - - if (volume < 0) - volume = 0; - - if (volume > 100) - volume = 100; - - /* - * slider quite often reads +-8, so debounce this random noise - */ - if (abs(volume - old_slider_volume) > 7) { - old_slider_volume = volume; - - if (debug_flg & DEBUG_MIXER) - printk(KERN_DEBUG "Slider volume: %d.\n", volume); - } - - return old_slider_volume; -} - -/* - * Decode a recording mask into a mixer selection on the NetWinder - * as follows: - * - * OSS Source WA Source Actual source - * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) - * SOUND_MASK_LINE Line Line in - * SOUND_MASK_LINE1 Left Mic Handset - * SOUND_MASK_PHONEIN Left Aux Telephone microphone - * SOUND_MASK_MIC Right Mic Builtin microphone - */ -static unsigned int -netwinder_select_input(struct wavnc_info *devc, unsigned int recmask, - unsigned char *dev_l, unsigned char *dev_r) -{ - unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE; - - if (recmask & SOUND_MASK_IMIX) { - recmask = SOUND_MASK_IMIX; - recdev_l = ADC_MUX_MIXER; - recdev_r = ADC_MUX_MIXER; - } else if (recmask & SOUND_MASK_LINE) { - recmask = SOUND_MASK_LINE; - recdev_l = ADC_MUX_LINE; - recdev_r = ADC_MUX_LINE; - } else if (recmask & SOUND_MASK_LINE1) { - recmask = SOUND_MASK_LINE1; - waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ - recdev_l = ADC_MUX_MIC; - recdev_r = ADC_MUX_NONE; - } else if (recmask & SOUND_MASK_PHONEIN) { - recmask = SOUND_MASK_PHONEIN; - waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ - recdev_l = ADC_MUX_AUX1; - recdev_r = ADC_MUX_NONE; - } else if (recmask & SOUND_MASK_MIC) { - recmask = SOUND_MASK_MIC; - waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */ - recdev_l = ADC_MUX_NONE; - recdev_r = ADC_MUX_MIC; - } - - *dev_l = recdev_l; - *dev_r = recdev_r; - - return recmask; -} - -static int -netwinder_decode_mixer(struct wavnc_info *devc, int dev, unsigned char lev_l, - unsigned char lev_r) -{ - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IGAIN: - devc->levels[dev] = lev_l | lev_r << 8; - break; - - case SOUND_MIXER_MIC: /* right mic only */ - devc->levels[SOUND_MIXER_MIC] &= 0xff; - devc->levels[SOUND_MIXER_MIC] |= lev_l << 8; - break; - - case SOUND_MIXER_LINE1: /* left mic only */ - devc->levels[SOUND_MIXER_MIC] &= 0xff00; - devc->levels[SOUND_MIXER_MIC] |= lev_l; - dev = SOUND_MIXER_MIC; - break; - - case SOUND_MIXER_PHONEIN: /* left aux only */ - devc->levels[SOUND_MIXER_LINE1] = lev_l; - dev = SOUND_MIXER_LINE1; - break; - - case SOUND_MIXER_IMIX: - case SOUND_MIXER_PHONEOUT: - break; - - default: - dev = -EINVAL; - break; - } - return dev; -} - -static int netwinder_get_mixer(struct wavnc_info *devc, int dev) -{ - int levels; - - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IGAIN: - levels = devc->levels[dev]; - break; - - case SOUND_MIXER_MIC: /* builtin mic: right mic only */ - levels = devc->levels[SOUND_MIXER_MIC] >> 8; - levels |= levels << 8; - break; - - case SOUND_MIXER_LINE1: /* handset mic: left mic only */ - levels = devc->levels[SOUND_MIXER_MIC] & 0xff; - levels |= levels << 8; - break; - - case SOUND_MIXER_PHONEIN: /* phone mic: left aux1 only */ - levels = devc->levels[SOUND_MIXER_LINE1] & 0xff; - levels |= levels << 8; - break; - - default: - levels = 0; - } - - return levels; -} - -/* - * Waveartist specific mixer information. - */ -static const struct waveartist_mixer_info netwinder_mixer = { - .supported_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | - SOUND_MASK_PCM | SOUND_MASK_SPEAKER | - SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_IMIX | SOUND_MASK_LINE1 | - SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT| - SOUND_MASK_IGAIN, - - .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_IMIX | SOUND_MASK_LINE1 | - SOUND_MASK_PHONEIN, - - .stereo_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | - SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_IMIX | SOUND_MASK_IGAIN, - - .select_input = netwinder_select_input, - .decode_mixer = netwinder_decode_mixer, - .get_mixer = netwinder_get_mixer, -}; - -static void -vnc_configure_mixer(struct wavnc_info *devc, unsigned int recmask) -{ - if (!devc->no_autoselect) { - if (devc->handset_detect) { - recmask = SOUND_MASK_LINE1; - devc->spkr_mute_state = devc->line_mute_state = 1; - } else if (devc->telephone_detect) { - recmask = SOUND_MASK_PHONEIN; - devc->spkr_mute_state = devc->line_mute_state = 1; - } else { - /* unless someone has asked for LINE-IN, - * we default to MIC - */ - if ((devc->recmask & SOUND_MASK_LINE) == 0) - devc->recmask = SOUND_MASK_MIC; - devc->spkr_mute_state = devc->line_mute_state = 0; - } - vnc_mute_spkr(devc); - vnc_mute_lout(devc); - - if (recmask != devc->recmask) - waveartist_set_recmask(devc, recmask); - } -} - -static int -vnc_slider(struct wavnc_info *devc) -{ - signed int slider_volume; - unsigned int temp, old_hs, old_td; - - /* - * read the "buttons" state. - * Bit 4 = 0 means handset present - * Bit 5 = 1 means phone offhook - */ - temp = inb(0x201); - - old_hs = devc->handset_detect; - old_td = devc->telephone_detect; - - devc->handset_detect = !(temp & 0x10); - devc->telephone_detect = !!(temp & 0x20); - - if (!devc->no_autoselect && - (old_hs != devc->handset_detect || - old_td != devc->telephone_detect)) - vnc_configure_mixer(devc, devc->recmask); - - slider_volume = vnc_volume_slider(devc); - - /* - * If we're using software controlled volume, and - * the slider moves by more than 20%, then we - * switch back to slider controlled volume. - */ - if (abs(devc->slider_vol - slider_volume) > 20) - devc->use_slider = 1; - - /* - * use only left channel - */ - temp = levels[SOUND_MIXER_VOLUME] & 0xFF; - - if (slider_volume != temp && devc->use_slider) { - devc->slider_vol = slider_volume; - - waveartist_set_mixer(devc, SOUND_MIXER_VOLUME, - slider_volume | slider_volume << 8); - - return 1; - } - - return 0; -} - -static void -vnc_slider_tick(unsigned long data) -{ - int next_timeout; - - if (vnc_slider(adev_info + data)) - next_timeout = 5; // mixer reported change - else - next_timeout = VNC_TIMER_PERIOD; - - mod_timer(&vnc_timer, jiffies + next_timeout); -} - -static int -vnc_private_ioctl(int dev, unsigned int cmd, int __user * arg) -{ - struct wavnc_info *devc = (struct wavnc_info *)audio_devs[dev]->devc; - int val; - - switch (cmd) { - case SOUND_MIXER_PRIVATE1: - { - u_int prev_spkr_mute, prev_line_mute, prev_auto_state; - int val; - - if (get_user(val, arg)) - return -EFAULT; - - /* check if parameter is logical */ - if (val & ~(VNC_MUTE_INTERNAL_SPKR | - VNC_MUTE_LINE_OUT | - VNC_DISABLE_AUTOSWITCH)) - return -EINVAL; - - prev_auto_state = devc->no_autoselect; - prev_spkr_mute = devc->spkr_mute_state; - prev_line_mute = devc->line_mute_state; - - devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0; - devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0; - devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0; - - if (prev_spkr_mute != devc->spkr_mute_state) - vnc_mute_spkr(devc); - - if (prev_line_mute != devc->line_mute_state) - vnc_mute_lout(devc); - - if (prev_auto_state != devc->no_autoselect) - vnc_configure_mixer(devc, devc->recmask); - - return 0; - } - - case SOUND_MIXER_PRIVATE2: - if (get_user(val, arg)) - return -EFAULT; - - switch (val) { -#define VNC_SOUND_PAUSE 0x53 //to pause the DSP -#define VNC_SOUND_RESUME 0x57 //to unpause the DSP - case VNC_SOUND_PAUSE: - waveartist_cmd1(devc, 0x16); - break; - - case VNC_SOUND_RESUME: - waveartist_cmd1(devc, 0x18); - break; - - default: - return -EINVAL; - } - return 0; - - /* private ioctl to allow bulk access to waveartist */ - case SOUND_MIXER_PRIVATE3: - { - unsigned long flags; - int mixer_reg[15], i, val; - - if (get_user(val, arg)) - return -EFAULT; - if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg))) - return -EFAULT; - - switch (mixer_reg[14]) { - case MIXER_PRIVATE3_RESET: - waveartist_mixer_reset(devc); - break; - - case MIXER_PRIVATE3_WRITE: - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]); - - waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]); - waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]); - break; - - case MIXER_PRIVATE3_READ: - spin_lock_irqsave(&waveartist_lock, flags); - - for (i = 0x30; i < 14 << 8; i += 1 << 8) - waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8)); - - spin_unlock_irqrestore(&waveartist_lock, flags); - - if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg))) - return -EFAULT; - break; - - default: - return -EINVAL; - } - return 0; - } - - /* read back the state from PRIVATE1 */ - case SOUND_MIXER_PRIVATE4: - val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) | - (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) | - (devc->handset_detect ? VNC_HANDSET_DETECT : 0) | - (devc->telephone_detect ? VNC_PHONE_DETECT : 0) | - (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0); - - return put_user(val, arg) ? -EFAULT : 0; - } - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - /* - * special case for master volume: if we - * received this call - switch from hw - * volume control to a software volume - * control, till the hw volume is modified - * to signal that user wants to be back in - * hardware... - */ - if ((cmd & 0xff) == SOUND_MIXER_VOLUME) - devc->use_slider = 0; - - /* speaker output */ - if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) { - unsigned int val, l, r; - - if (get_user(val, arg)) - return -EFAULT; - - l = val & 0x7f; - r = (val & 0x7f00) >> 8; - val = (l + r) / 2; - devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8); - devc->spkr_mute_state = (val <= 50); - vnc_mute_spkr(devc); - return 0; - } - } - - return -ENOIOCTLCMD; -} - -#endif - -static struct address_info cfg; - -static int attached; - -static int __initdata io = 0; -static int __initdata irq = 0; -static int __initdata dma = 0; -static int __initdata dma2 = 0; - - -static int __init init_waveartist(void) -{ - const struct waveartist_mixer_info *mix; - - if (!io && machine_is_netwinder()) { - /* - * The NetWinder WaveArtist is at a fixed address. - * If the user does not supply an address, use the - * well-known parameters. - */ - io = 0x250; - irq = 12; - dma = 3; - dma2 = 7; - } - - mix = &waveartist_mixer; -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) - mix = &netwinder_mixer; -#endif - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - if (!probe_waveartist(&cfg)) - return -ENODEV; - - attach_waveartist(&cfg, mix); - attached = 1; - - return 0; -} - -static void __exit cleanup_waveartist(void) -{ - if (attached) - unload_waveartist(&cfg); -} - -module_init(init_waveartist); -module_exit(cleanup_waveartist); - -#ifndef MODULE -static int __init setup_waveartist(char *str) -{ - /* io, irq, dma, dma2 */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - - return 1; -} -__setup("waveartist=", setup_waveartist); -#endif - -MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver"); -module_param_hw(io, int, ioport, 0); /* IO base */ -module_param_hw(irq, int, irq, 0); /* IRQ */ -module_param_hw(dma, int, dma, 0); /* DMA */ -module_param_hw(dma2, int, dma, 0); /* DMA2 */ -MODULE_LICENSE("GPL"); |