📄 waveartist.c
字号:
/* * linux/drivers/sound/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/config.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/spinlock.h>#include <linux/bitops.h>#include <asm/system.h>#include "sound_config.h"#include "waveartist.h"#ifdef CONFIG_ARM#include <asm/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 */};typedef struct { 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} wavnc_info;/* * 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)(wavnc_info *, unsigned int, unsigned char *, unsigned char *); int (*decode_mixer)(wavnc_info *, int, unsigned char, unsigned char); int (*get_mixer)(wavnc_info *, int);};typedef struct wavnc_port_info { int open_mode; int speed; int channels; int audio_format;} wavnc_port_info;static int nr_waveartist_devs;static wavnc_info adev_info[MAX_AUDIO_DEV];static spinlock_t waveartist_lock = SPIN_LOCK_UNLOCKED;#ifndef CONFIG_ARCH_NETWINDER#define machine_is_netwinder() 0#elsestatic struct timer_list vnc_timer;static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask);static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg);static void vnc_slider_tick(unsigned long data);#endifstatic inline voidwaveartist_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 voidwaveartist_iack(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 intwaveartist_sleep(int timeout_ms){ unsigned int timeout = timeout_ms * 10 * HZ / 100; do { set_current_state(TASK_INTERRUPTIBLE); timeout = schedule_timeout(timeout); } while (timeout); return 0;}static intwaveartist_reset(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 intwaveartist_cmd(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"); } /* * flush any stale command data from the port. */ while (inb(io_base + STATR) & CMD_RF) { unsigned int old_data; old_data = inw(io_base + CMDR); printk("waveartist: flushing stale command data: 0x%04x pc=%p\n", old_data, __builtin_return_address(0)); 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 && !timed_out) { printk("waveartist_cmd: resp="); for (i = 0; i < nr_resp; i++) printk("%04X ", resp[i]); printk("\n"); } if (timed_out) { printk(KERN_ERR "waveartist_cmd: command timed out:"); for (i = 0; i < nr_cmd; i++) printk(" %04x", cmd[i]); printk("\n"); } return timed_out ? 1 : 0;}/* * Send one command word */static inline intwaveartist_cmd1(wavnc_info *devc, unsigned int cmd){ return waveartist_cmd(devc, 1, &cmd, 0, NULL);}/* * Send one command, receive one word */static inline unsigned intwaveartist_cmd1_r(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 intwaveartist_cmd2(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 intwaveartist_cmd3(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 intwaveartist_getrev(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 intwaveartist_open(int dev, int mode){ wavnc_info *devc; wavnc_port_info *portc; unsigned long flags; if (dev < 0 || dev >= num_audiodevs) return -ENXIO; devc = (wavnc_info *) audio_devs[dev]->devc; portc = (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 voidwaveartist_close(int dev){ wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; wavnc_port_info *portc = (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 voidwaveartist_output_block(int dev, unsigned long buf, int __count, int intrflag){ wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; wavnc_info *devc = (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 voidwaveartist_start_input(int dev, unsigned long buf, int __count, int intrflag){ wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; wavnc_info *devc = (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) { return; /* * Auto DMA mode on. No need to react */ } spin_lock_irqsave(&waveartist_lock, flags); /* * set sample count */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -