📄 waveartist.c
字号:
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;}/* * 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 intnetwinder_select_input(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 intnetwinder_decode_mixer(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(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 voidvnc_configure_mixer(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 intvnc_slider(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 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_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, (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 (_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, (int *)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;}#endifstatic 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 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]; dma2 = ints[4]; return 1;}__setup("waveartist=", setup_waveartist);#endifMODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver");MODULE_PARM(io, "i"); /* IO base */MODULE_PARM(irq, "i"); /* IRQ */MODULE_PARM(dma, "i"); /* DMA */MODULE_PARM(dma2, "i"); /* DMA2 */MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -