📄 waveartist.c
字号:
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){ 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 (check_region(hw_config->io_base, 15)) { printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); return 0; } if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) { printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", hw_config->irq); return 0; } if (hw_config->dma != _ISA_DMA(3)) { 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){ 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; request_region(hw->io_base, 15, devc->hw.name); 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()) { init_timer(&vnc_timer); vnc_timer.function = vnc_slider_tick; vnc_timer.expires = jiffies; vnc_timer.data = nr_waveartist_devs; add_timer(&vnc_timer); vnc_configure_mixer(devc); }#endif nr_waveartist_devs += 1; }}static void __exit unload_waveartist(struct address_info *hw){ 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");}/* * Rebel.com Netwinder specifics... */#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 0x80extern spinlock_t gpio_lock;static inline voidvnc_update_spkr_mute(wavnc_info *devc){ unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE); spin_unlock_irqrestore(&gpio_lock, flags);}static voidvnc_mute_lout(wavnc_info *devc, int mute){}static intvnc_volume_slider(wavnc_info *devc){ static signed int old_slider_volume; unsigned long flags; signed int volume = 255; *CSR_TIMER1_LOAD = 0x00ffffff; save_flags(flags); cli(); outb(0xFF, 0x201); *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; while (volume && (inb(0x201) & 0x01)) volume--; *CSR_TIMER1_CNTL = 0; restore_flags(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;}static voidvnc_configure_mixer(wavnc_info *devc){ u_int vals[3]; if (!devc->no_autoselect) { if (devc->handset_detect) { devc->recmask = SOUND_MASK_LINE1; devc->spkr_mute_state = devc->line_mute_state = 1; } else if (devc->telephone_detect) { devc->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_update_spkr_mute(devc); vnc_mute_lout(devc, devc->spkr_mute_state); } /* Ok. At this point, we have done the autoswitch logic, or we * have had a command from an ioctl. We have a valid devc->recmask. * Now we have to connect up the hardware to reflect the recmask. */ vals[1] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x800); vals[2] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x900); vals[1] &= ~0x3f; switch(devc->recmask) { case SOUND_MASK_MIC: /* builtin mic */ waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */ vals[1] |= 0x28; break; case SOUND_MASK_LINE1: /* out handset */ waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ vals[1] |= 0x05; break; case SOUND_MASK_PHONEIN: /* our telephone mic */ waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ vals[1] |= 0x04; break; case SOUND_MASK_LINE: /* stereo line in */ vals[1] |= 12; break; default: return; } vals[0] = WACMD_SET_MIXER; waveartist_cmd(devc, 3, vals, 0, NULL); waveartist_mixer_update(devc, SOUND_MIXER_IMIX);}static intvnc_slider(wavnc_info *devc){ signed int slider_volume; unsigned int temp, old_hs, old_td; /* * read the "buttons" state. * Bit 4 = handset present, * Bit 5 = offhook */ temp = inb(0x201) & 0x30; 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); 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_mixer_set(devc, SOUND_MIXER_VOLUME, slider_volume | slider_volume << 8); return 1; } return 0;}static voidvnc_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 intvnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg){ wavnc_info *devc = (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, (int *)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_update_spkr_mute(devc); if (prev_line_mute != devc->line_mute_state) vnc_mute_lout(devc, devc->line_mute_state); if (prev_auto_state != devc->no_autoselect) vnc_configure_mixer(devc); return 0; } case SOUND_MIXER_PRIVATE2: if (get_user(val, (int *)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, (int *)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, (int *)arg) ? -EFAULT : 0; } if (((cmd >> 8) & 0xff) == 'M') { 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; } else if ((cmd & 0xff) == SOUND_MIXER_STEREODEVS) { val = devc->supported_devices & ~(SOUND_MASK_IMIX | SOUND_MASK_MIC | SOUND_MASK_LINE1 | SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT); return put_user(val, (int *)arg) ? -EFAULT : 0; } } return -ENOIOCTLCMD;}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;MODULE_PARM(io, "i"); /* IO base */MODULE_PARM(irq, "i"); /* IRQ */MODULE_PARM(dma, "i"); /* DMA */MODULE_PARM(dma2, "i"); /* DMA2 */static int __init init_waveartist(void){ 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; } cfg.io_base = io; cfg.irq = irq; cfg.dma = dma; cfg.dma2 = dma2; if (!probe_waveartist(&cfg)) return -ENODEV; attach_waveartist(&cfg); attached = 1; return 0;}static void __exit cleanup_waveartist(void){ if (attached) unload_waveartist(&cfg);}module_init(init_waveartist);module_exit(cleanup_waveartist);#ifndef MODULEstatic 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]; dma16 = ints[4]; return 1;}__setup("waveartist=", setup_waveartist);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -