📄 harmony.c
字号:
*/static void harmony_mixer_set_gain(void){ harmony_wait_CNTL(); gsc_writel(harmony.current_gain, &harmony.hpa->gainctl);}/* * Read gain of selected channel. * The OSS rate is from 0 (silent) to 100 -> need some conversions * * The harmony gain are attenuation for output and monitor gain. * is amplifaction for input gain */#define to_harmony_level(level,max) ((level)*max/100)#define to_oss_level(level,max) ((level)*100/max)static int harmony_mixer_get_level(int channel){ int left_level; int right_level; switch (channel) { case SOUND_MIXER_VOLUME: left_level = (harmony.current_gain & GAIN_LO_MASK) >> GAIN_LO_SHIFT; right_level = (harmony.current_gain & GAIN_RO_MASK) >> GAIN_RO_SHIFT; left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); return (right_level << 8)+left_level; case SOUND_MIXER_IGAIN: left_level = (harmony.current_gain & GAIN_LI_MASK) >> GAIN_LI_SHIFT; right_level= (harmony.current_gain & GAIN_RI_MASK) >> GAIN_RI_SHIFT; left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); right_level= to_oss_level(right_level, MAX_INPUT_LEVEL); return (right_level << 8)+left_level; case SOUND_MIXER_MONITOR: left_level = (harmony.current_gain & GAIN_MA_MASK) >> GAIN_MA_SHIFT; left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL); return (left_level << 8)+left_level; } return -EINVAL;}/* * Some conversions for the same reasons. * We give back the new real value(s) due to * the rescale. */static int harmony_mixer_set_level(int channel, int value){ int left_level; int right_level; int new_left_level; int new_right_level; right_level = (value & 0x0000ff00) >> 8; left_level = value & 0x000000ff; if (right_level > 100) right_level = 100; if (left_level > 100) left_level = 100; switch (channel) { case SOUND_MIXER_VOLUME: right_level = to_harmony_level(100-right_level, MAX_OUTPUT_LEVEL); left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL); new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); new_left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); harmony.current_gain = (harmony.current_gain & ~(GAIN_LO_MASK | GAIN_RO_MASK)) | (left_level << GAIN_LO_SHIFT) | (right_level << GAIN_RO_SHIFT); harmony_mixer_set_gain(); return (new_right_level << 8) + new_left_level; case SOUND_MIXER_IGAIN: right_level = to_harmony_level(right_level, MAX_INPUT_LEVEL); left_level = to_harmony_level(left_level, MAX_INPUT_LEVEL); new_right_level = to_oss_level(right_level, MAX_INPUT_LEVEL); new_left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); harmony.current_gain = (harmony.current_gain & ~(GAIN_LI_MASK | GAIN_RI_MASK)) | (left_level << GAIN_LI_SHIFT) | (right_level << GAIN_RI_SHIFT); harmony_mixer_set_gain(); return (new_right_level << 8) + new_left_level; case SOUND_MIXER_MONITOR: left_level = to_harmony_level(100-left_level, MAX_MONITOR_LEVEL); new_left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL); harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK) | (left_level << GAIN_MA_SHIFT); harmony_mixer_set_gain(); return (new_left_level << 8) + new_left_level; } return -EINVAL;}#undef to_harmony_level#undef to_oss_level/* * Return the selected input device (mic or line) */static int harmony_mixer_get_recmask(void) { int current_input_line; current_input_line = (harmony.current_gain & GAIN_IS_MASK) >> GAIN_IS_SHIFT; if (current_input_line) return SOUND_MASK_MIC; return SOUND_MASK_LINE;}/* * Set the input (only one at time, arbitrary priority to line in) */static int harmony_mixer_set_recmask(int recmask){ int new_input_line; int new_input_mask; int current_input_line; current_input_line = (harmony.current_gain & GAIN_IS_MASK) >> GAIN_IS_SHIFT; if ((current_input_line && ((recmask & SOUND_MASK_LINE) || !(recmask & SOUND_MASK_MIC))) || (!current_input_line && ((recmask & SOUND_MASK_LINE) && !(recmask & SOUND_MASK_MIC)))) { new_input_line = 0; new_input_mask = SOUND_MASK_LINE; } else { new_input_line = 1; new_input_mask = SOUND_MASK_MIC; } harmony.current_gain = ((harmony.current_gain & ~GAIN_IS_MASK) | (new_input_line << GAIN_IS_SHIFT )); harmony_mixer_set_gain(); return new_input_mask;}/* * give the active outlines */static int harmony_mixer_get_outmask(void){ int outmask = 0; if (harmony.current_gain & GAIN_SE_MASK) outmask |= MASK_INTERNAL; if (harmony.current_gain & GAIN_LE_MASK) outmask |= MASK_LINEOUT; if (harmony.current_gain & GAIN_HE_MASK) outmask |= MASK_HEADPHONES; return outmask;}static int harmony_mixer_set_outmask(int outmask){ if (outmask & MASK_INTERNAL) harmony.current_gain |= GAIN_SE_MASK; else harmony.current_gain &= ~GAIN_SE_MASK; if (outmask & MASK_LINEOUT) harmony.current_gain |= GAIN_LE_MASK; else harmony.current_gain &= ~GAIN_LE_MASK; if (outmask & MASK_HEADPHONES) harmony.current_gain |= GAIN_HE_MASK; else harmony.current_gain &= ~GAIN_HE_MASK; harmony_mixer_set_gain(); return (outmask & (MASK_INTERNAL | MASK_LINEOUT | MASK_HEADPHONES));}/* * This code is inspired from sb_mixer.c */static int harmony_mixer_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ int val; int ret; if (cmd == SOUND_MIXER_INFO) { mixer_info info; memset(&info, 0, sizeof(info)); strncpy(info.id, "harmony", sizeof(info.id)-1); strncpy(info.name, "Harmony audio", sizeof(info.name)-1); info.modify_counter = 1; /* ? */ if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int *)arg); /* read */ val = 0; if (_SIOC_DIR(cmd) & _SIOC_WRITE) if (get_user(val, (int *)arg)) return -EFAULT; switch (cmd) { case MIXER_READ(SOUND_MIXER_CAPS): ret = SOUND_CAP_EXCL_INPUT; break; case MIXER_READ(SOUND_MIXER_STEREODEVS): ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN; break; case MIXER_READ(SOUND_MIXER_RECMASK): ret = SOUND_MASK_MIC | SOUND_MASK_LINE; break; case MIXER_READ(SOUND_MIXER_DEVMASK): ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN | SOUND_MASK_MONITOR; break; case MIXER_READ(SOUND_MIXER_OUTMASK): ret = MASK_INTERNAL | MASK_LINEOUT | MASK_HEADPHONES; break; case MIXER_WRITE(SOUND_MIXER_RECSRC): ret = harmony_mixer_set_recmask(val); break; case MIXER_READ(SOUND_MIXER_RECSRC): ret = harmony_mixer_get_recmask(); break; case MIXER_WRITE(SOUND_MIXER_OUTSRC): ret = harmony_mixer_set_outmask(val); break; case MIXER_READ(SOUND_MIXER_OUTSRC): ret = harmony_mixer_get_outmask(); break; case MIXER_WRITE(SOUND_MIXER_VOLUME): case MIXER_WRITE(SOUND_MIXER_IGAIN): case MIXER_WRITE(SOUND_MIXER_MONITOR): ret = harmony_mixer_set_level(cmd & 0xff, val); break; case MIXER_READ(SOUND_MIXER_VOLUME): case MIXER_READ(SOUND_MIXER_IGAIN): case MIXER_READ(SOUND_MIXER_MONITOR): ret = harmony_mixer_get_level(cmd & 0xff); break; default: return -EINVAL; } if (put_user(ret, (int *)arg)) return -EFAULT; return 0;}static int harmony_mixer_open(struct inode *inode, struct file *file){ if (harmony.mixer_open) return -EBUSY; harmony.mixer_open = 1; return 0;}static int harmony_mixer_release(struct inode *inode, struct file *file){ if (!harmony.mixer_open) return -EBUSY; harmony.mixer_open = 0; return 0;}static struct file_operations harmony_mixer_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = harmony_mixer_open, .release = harmony_mixer_release, .ioctl = harmony_mixer_ioctl,};/* * Mute all the output and reset Harmony. */static void __init harmony_mixer_reset(void){ harmony.current_gain = GAIN_TOTAL_SILENCE; harmony_mixer_set_gain(); harmony_wait_CNTL(); gsc_writel(1, &harmony.hpa->reset); mdelay(50); /* wait 50 ms */ gsc_writel(0, &harmony.hpa->reset); harmony.current_gain = GAIN_DEFAULT; harmony_mixer_set_gain();}static int __init harmony_mixer_init(void){ /* Register the device file operations */ harmony.mixer_unit = register_sound_mixer(&harmony_mixer_fops, -1); if (harmony.mixer_unit < 0) { printk(KERN_WARNING PFX "Error Registering Mixer Driver\n"); return -EFAULT; } harmony_mixer_reset(); harmony.mixer_open = 0; return 0;}/* * This is the callback that's called by the inventory hardware code * if it finds a match to the registered driver. */static int __devinitharmony_driver_probe(struct parisc_device *dev){ u8 id; u8 rev; u32 cntl; int ret; if (harmony.hpa) { /* We only support one Harmony at this time */ printk(KERN_ERR PFX "driver already registered\n"); return -EBUSY; } if (!dev->irq) { printk(KERN_ERR PFX "no irq found\n"); return -ENODEV; } /* Set the HPA of harmony */ harmony.hpa = (struct harmony_hpa *)dev->hpa; harmony.dev = dev; /* Grab the ID and revision from the device */ id = gsc_readb(&harmony.hpa->id); if ((id | 1) != 0x15) { printk(KERN_WARNING PFX "wrong harmony id 0x%02x\n", id); return -EBUSY; } cntl = gsc_readl(&harmony.hpa->cntl); rev = (cntl>>20) & 0xff; printk(KERN_INFO "Lasi Harmony Audio driver " HARMONY_VERSION ", " "h/w id %i, rev. %i at 0x%lx, IRQ %i\n", id, rev, dev->hpa, harmony.dev->irq); /* Make sure the control bit isn't set, although I don't think it ever is. */ if (cntl & CNTL_C) { printk(KERN_WARNING PFX "CNTL busy\n"); harmony.hpa = 0; return -EBUSY; } /* Initialize the memory buffers */ if (harmony_alloc_buffer(&played_buf, MAX_BUFS) || harmony_alloc_buffer(&recorded_buf, MAX_BUFS) || harmony_alloc_buffer(&graveyard, 1) || harmony_alloc_buffer(&silent, 1)) { ret = -EBUSY; goto out_err; } /* Initialize /dev/mixer and /dev/audio */ if ((ret=harmony_mixer_init())) goto out_err; if ((ret=harmony_audio_init())) goto out_err; return 0;out_err: harmony.hpa = 0; harmony_free_buffer(&played_buf); harmony_free_buffer(&recorded_buf); harmony_free_buffer(&graveyard); harmony_free_buffer(&silent); return ret;}static struct parisc_device_id harmony_tbl[] = { /* { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, Bushmaster/Flounder */ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, /* 712/715 Audio */ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, /* Pace Audio */ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, /* Outfield / Coral II */ { 0, }};MODULE_DEVICE_TABLE(parisc, harmony_tbl);static struct parisc_driver harmony_driver = { .name = "Lasi Harmony", .id_table = harmony_tbl, .probe = harmony_driver_probe,};static int __init init_harmony(void){ return register_parisc_driver(&harmony_driver);}static void __exit cleanup_harmony(void){ free_irq(harmony.dev->irq, &harmony); unregister_sound_mixer(harmony.mixer_unit); unregister_sound_dsp(harmony.dsp_unit); harmony_free_buffer(&played_buf); harmony_free_buffer(&recorded_buf); harmony_free_buffer(&graveyard); harmony_free_buffer(&silent); unregister_parisc_driver(&harmony_driver);}MODULE_AUTHOR("Alex DeVries <alex@onefishtwo.ca>");MODULE_DESCRIPTION("Harmony sound driver");MODULE_LICENSE("GPL");module_init(init_harmony);module_exit(cleanup_harmony);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -