📄 ad1848.c
字号:
int right = (value & 0x0000ff00) >> 8; int retvol; if (dev > 31) return -EINVAL; if (!(devc->supported_devices & (1 << dev))) return -EINVAL; dev = devc->mixer_reroute[dev]; if (devc->mix_devices[dev][LEFT_CHN].nbits == 0) return -EINVAL; if (left > 100) left = 100; if (right > 100) right = 100; if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ right = left; retvol = left | (right << 8); /* Scale volumes */ left = mix_cvt[left]; right = mix_cvt[right]; devc->levels[dev] = retvol; /* * Set the left channel */ ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN); /* * Set the right channel */ if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) goto out; ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN); out: return retvol;}static void ad1848_mixer_reset(ad1848_info * devc){ int i; char name[32]; unsigned long flags; devc->mix_devices = &(ad1848_mix_devices[0]); sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs); for (i = 0; i < 32; i++) devc->mixer_reroute[i] = i; devc->supported_rec_devices = MODE1_REC_DEVICES; switch (devc->model) { case MD_4231: case MD_4231A: case MD_1845: case MD_1845_SSCAPE: devc->supported_devices = MODE2_MIXER_DEVICES; break; case MD_C930: devc->supported_devices = C930_MIXER_DEVICES; devc->mix_devices = &(c930_mix_devices[0]); break; case MD_IWAVE: devc->supported_devices = MODE3_MIXER_DEVICES; devc->mix_devices = &(iwave_mix_devices[0]); break; case MD_42xB: case MD_4239: devc->mix_devices = &(cs42xb_mix_devices[0]); devc->supported_devices = MODE3_MIXER_DEVICES; break; case MD_4232: case MD_4235: case MD_4236: devc->supported_devices = MODE3_MIXER_DEVICES; break; case MD_1848: if (soundpro) { devc->supported_devices = SPRO_MIXER_DEVICES; devc->supported_rec_devices = SPRO_REC_DEVICES; devc->mix_devices = &(spro_mix_devices[0]); break; } default: devc->supported_devices = MODE1_MIXER_DEVICES; } devc->orig_devices = devc->supported_devices; devc->orig_rec_devices = devc->supported_rec_devices; devc->levels = load_mixer_volumes(name, default_mixer_levels, 1); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (devc->supported_devices & (1 << i)) ad1848_mixer_set(devc, i, devc->levels[i]); } ad1848_set_recmask(devc, SOUND_MASK_MIC); devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT; spin_lock_irqsave(&devc->lock,flags); if (!soundpro) { if (devc->mixer_output_port & AUDIO_SPEAKER) ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ else ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ } else { /* * From the "wouldn't it be nice if the mixer API had (better) * support for custom stuff" category */ /* Enable surround mode and SB16 mixer */ ad_write(devc, 16, 0x60); } spin_unlock_irqrestore(&devc->lock,flags);}static int ad1848_mixer_ioctl(int dev, unsigned int cmd, void __user *arg){ ad1848_info *devc = mixer_devs[dev]->devc; int val; if (cmd == SOUND_MIXER_PRIVATE1) { if (get_user(val, (int __user *)arg)) return -EFAULT; if (val != 0xffff) { unsigned long flags; val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT); devc->mixer_output_port = val; val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */ devc->mixer_output_port = val; spin_lock_irqsave(&devc->lock,flags); if (val & AUDIO_SPEAKER) ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ else ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ spin_unlock_irqrestore(&devc->lock,flags); } val = devc->mixer_output_port; return put_user(val, (int __user *)arg); } if (cmd == SOUND_MIXER_PRIVATE2) { if (get_user(val, (int __user *)arg)) return -EFAULT; return(ad1848_control(AD1848_MIXER_REROUTE, val)); } if (((cmd >> 8) & 0xff) == 'M') { if (_SIOC_DIR(cmd) & _SIOC_WRITE) { switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: if (get_user(val, (int __user *)arg)) return -EFAULT; val = ad1848_set_recmask(devc, val); break; default: if (get_user(val, (int __user *)arg)) return -EFAULT; val = ad1848_mixer_set(devc, cmd & 0xff, val); break; } return put_user(val, (int __user *)arg); } else { switch (cmd & 0xff) { /* * Return parameters */ case SOUND_MIXER_RECSRC: val = devc->recmask; break; case SOUND_MIXER_DEVMASK: val = devc->supported_devices; break; case SOUND_MIXER_STEREODEVS: val = devc->supported_devices; if (devc->model != MD_C930) val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); break; case SOUND_MIXER_RECMASK: val = devc->supported_rec_devices; break; case SOUND_MIXER_CAPS: val=SOUND_CAP_EXCL_INPUT; break; default: val = ad1848_mixer_get(devc, cmd & 0xff); break; } return put_user(val, (int __user *)arg); } } else return -EINVAL;}static int ad1848_set_speed(int dev, int arg){ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; /* * The sampling speed is encoded in the least significant nibble of I8. The * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other * three bits select the divisor (indirectly): * * The available speeds are in the following table. Keep the speeds in * the increasing order. */ typedef struct { int speed; unsigned char bits; } speed_struct; static speed_struct speed_table[] = { {5510, (0 << 1) | 1}, {5510, (0 << 1) | 1}, {6620, (7 << 1) | 1}, {8000, (0 << 1) | 0}, {9600, (7 << 1) | 0}, {11025, (1 << 1) | 1}, {16000, (1 << 1) | 0}, {18900, (2 << 1) | 1}, {22050, (3 << 1) | 1}, {27420, (2 << 1) | 0}, {32000, (3 << 1) | 0}, {33075, (6 << 1) | 1}, {37800, (4 << 1) | 1}, {44100, (5 << 1) | 1}, {48000, (6 << 1) | 0} }; int i, n, selected = -1; n = sizeof(speed_table) / sizeof(speed_struct); if (arg <= 0) return portc->speed; if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */ { if (arg < 4000) arg = 4000; if (arg > 50000) arg = 50000; portc->speed = arg; portc->speed_bits = speed_table[3].bits; return portc->speed; } if (arg < speed_table[0].speed) selected = 0; if (arg > speed_table[n - 1].speed) selected = n - 1; for (i = 1 /*really */ ; selected == -1 && i < n; i++) { if (speed_table[i].speed == arg) selected = i; else if (speed_table[i].speed > arg) { int diff1, diff2; diff1 = arg - speed_table[i - 1].speed; diff2 = speed_table[i].speed - arg; if (diff1 < diff2) selected = i - 1; else selected = i; } } if (selected == -1) { printk(KERN_WARNING "ad1848: Can't find speed???\n"); selected = 3; } portc->speed = speed_table[selected].speed; portc->speed_bits = speed_table[selected].bits; return portc->speed;}static short ad1848_set_channels(int dev, short arg){ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; if (arg != 1 && arg != 2) return portc->channels; portc->channels = arg; return arg;}static unsigned int ad1848_set_bits(int dev, unsigned int arg){ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; static struct format_tbl { int format; unsigned char bits; } format2bits[] = { { 0, 0 } , { AFMT_MU_LAW, 1 } , { AFMT_A_LAW, 3 } , { AFMT_IMA_ADPCM, 5 } , { AFMT_U8, 0 } , { AFMT_S16_LE, 2 } , { AFMT_S16_BE, 6 } , { AFMT_S8, 0 } , { AFMT_U16_LE, 0 } , { AFMT_U16_BE, 0 } }; int i, n = sizeof(format2bits) / sizeof(struct format_tbl); if (arg == 0) return portc->audio_format; if (!(arg & ad_format_mask[devc->model])) arg = AFMT_U8; portc->audio_format = arg; for (i = 0; i < n; i++) if (format2bits[i].format == arg) { if ((portc->format_bits = format2bits[i].bits) == 0) return portc->audio_format = AFMT_U8; /* Was not supported */ return arg; } /* Still hanging here. Something must be terribly wrong */ portc->format_bits = 0; return portc->audio_format = AFMT_U8;}static struct audio_driver ad1848_audio_driver ={ .owner = THIS_MODULE, .open = ad1848_open, .close = ad1848_close, .output_block = ad1848_output_block, .start_input = ad1848_start_input, .prepare_for_input = ad1848_prepare_for_input, .prepare_for_output = ad1848_prepare_for_output, .halt_io = ad1848_halt, .halt_input = ad1848_halt_input, .halt_output = ad1848_halt_output, .trigger = ad1848_trigger, .set_speed = ad1848_set_speed, .set_bits = ad1848_set_bits, .set_channels = ad1848_set_channels};static struct mixer_operations ad1848_mixer_operations ={ .owner = THIS_MODULE, .id = "SOUNDPORT", .name = "AD1848/CS4248/CS4231", .ioctl = ad1848_mixer_ioctl};static int ad1848_open(int dev, int mode){ ad1848_info *devc; ad1848_port_info *portc; unsigned long flags; if (dev < 0 || dev >= num_audiodevs) return -ENXIO; devc = (ad1848_info *) audio_devs[dev]->devc; portc = (ad1848_port_info *) audio_devs[dev]->portc; /* here we don't have to protect against intr */ spin_lock(&devc->lock); if (portc->open_mode || (devc->open_mode & mode)) { spin_unlock(&devc->lock); return -EBUSY; } devc->dual_dma = 0; if (audio_devs[dev]->flags & DMA_DUPLEX) { devc->dual_dma = 1; } devc->intr_active = 0; devc->audio_mode = 0; devc->open_mode |= mode; portc->open_mode = mode; spin_unlock(&devc->lock); ad1848_trigger(dev, 0); if (mode & OPEN_READ) devc->record_dev = dev; if (mode & OPEN_WRITE) devc->playback_dev = dev;/* * Mute output until the playback really starts. This decreases clicking (hope so). */ spin_lock_irqsave(&devc->lock,flags); ad_mute(devc); spin_unlock_irqrestore(&devc->lock,flags); return 0;}static void ad1848_close(int dev){ unsigned long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; DEB(printk("ad1848_close(void)\n")); devc->intr_active = 0; ad1848_halt(dev); spin_lock_irqsave(&devc->lock,flags); devc->audio_mode = 0; devc->open_mode &= ~portc->open_mode; portc->open_mode = 0; ad_unmute(devc); spin_unlock_irqrestore(&devc->lock,flags);}static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag){ unsigned long flags, cnt; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; cnt = count; if (portc->audio_format == AFMT_IMA_ADPCM) { cnt /= 4; } else { if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; } if (portc->channels > 1) cnt >>= 1; cnt--; if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && intrflag && cnt == devc->xfer_count) { devc->audio_mode |= PCM_ENABLE_OUTPUT; devc->intr_active = 1; return; /* * Auto DMA mode on. No need to react */ } spin_lock_irqsave(&devc->lock,flags); ad_write(devc, 15, (unsigned char) (cnt & 0xff)); ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); devc->xfer_count = cnt; devc->audio_mode |= PCM_ENABLE_OUTPUT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -