📄 cmipci.c
字号:
/* * preparation *//* playback - enable spdif only on the certain condition */static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); int rate = substream->runtime->rate; int err, do_spdif, do_ac3 = 0; do_spdif = (rate >= 44100 && substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && substream->runtime->channels == 2); if (do_spdif && cm->can_ac3_hw) do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; if ((err = setup_spdif_playback(cm, substream, do_spdif, do_ac3)) < 0) return err; return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);}/* playback (via device #2) - enable spdif always */static int snd_cmipci_playback_spdif_prepare(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); int err, do_ac3; if (cm->can_ac3_hw) do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; else do_ac3 = 1; /* doesn't matter */ if ((err = setup_spdif_playback(cm, substream, 1, do_ac3)) < 0) return err; return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);}/* * Apparently, the samples last played on channel A stay in some buffer, even * after the channel is reset, and get added to the data for the rear DACs when * playing a multichannel stream on channel B. This is likely to generate * wraparounds and thus distortions. * To avoid this, we play at least one zero sample after the actual stream has * stopped. */static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec){ struct snd_pcm_runtime *runtime = rec->substream->runtime; unsigned int reg, val; if (rec->needs_silencing && runtime && runtime->dma_area) { /* set up a small silence buffer */ memset(runtime->dma_area, 0, PAGE_SIZE); reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; val = ((PAGE_SIZE / 4) - 1) | (((PAGE_SIZE / 4) / 2 - 1) << 16); snd_cmipci_write(cm, reg, val); /* configure for 16 bits, 2 channels, 8 kHz */ if (runtime->channels > 2) set_dac_channels(cm, rec, 2); spin_lock_irq(&cm->reg_lock); val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); val &= ~(CM_ASFC_MASK << (rec->ch * 3)); val |= (4 << CM_ASFC_SHIFT) << (rec->ch * 3); snd_cmipci_write(cm, CM_REG_FUNCTRL1, val); val = snd_cmipci_read(cm, CM_REG_CHFORMAT); val &= ~(CM_CH0FMT_MASK << (rec->ch * 2)); val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2); if (cm->chip_version == 68) { val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2)); val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2)); } snd_cmipci_write(cm, CM_REG_CHFORMAT, val); /* start stream (we don't need interrupts) */ cm->ctrl |= CM_CHEN0 << rec->ch; snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); spin_unlock_irq(&cm->reg_lock); msleep(1); /* stop and reset stream */ spin_lock_irq(&cm->reg_lock); cm->ctrl &= ~(CM_CHEN0 << rec->ch); val = CM_RST_CH0 << rec->ch; snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | val); snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~val); spin_unlock_irq(&cm->reg_lock); rec->needs_silencing = 0; }}static int snd_cmipci_playback_hw_free(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); setup_spdif_playback(cm, substream, 0, 0); restore_mixer_state(cm); snd_cmipci_silence_hack(cm, &cm->channel[0]); return snd_cmipci_hw_free(substream);}static int snd_cmipci_playback2_hw_free(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); snd_cmipci_silence_hack(cm, &cm->channel[1]); return snd_cmipci_hw_free(substream);}/* capture */static int snd_cmipci_capture_prepare(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);}/* capture with spdif (via device #2) */static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); spin_unlock_irq(&cm->reg_lock); return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);}static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs){ struct cmipci *cm = snd_pcm_substream_chip(subs); spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); spin_unlock_irq(&cm->reg_lock); return snd_cmipci_hw_free(subs);}/* * interrupt handler */static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id){ struct cmipci *cm = dev_id; unsigned int status, mask = 0; /* fastpath out, to ease interrupt sharing */ status = snd_cmipci_read(cm, CM_REG_INT_STATUS); if (!(status & CM_INTR)) return IRQ_NONE; /* acknowledge interrupt */ spin_lock(&cm->reg_lock); if (status & CM_CHINT0) mask |= CM_CH0_INT_EN; if (status & CM_CHINT1) mask |= CM_CH1_INT_EN; snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask); snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask); spin_unlock(&cm->reg_lock); if (cm->rmidi && (status & CM_UARTINT)) snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data); if (cm->pcm) { if ((status & CM_CHINT0) && cm->channel[0].running) snd_pcm_period_elapsed(cm->channel[0].substream); if ((status & CM_CHINT1) && cm->channel[1].running) snd_pcm_period_elapsed(cm->channel[1].substream); } return IRQ_HANDLED;}/* * h/w infos *//* playback on channel A */static struct snd_pcm_hardware snd_cmipci_playback ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, .rate_min = 5512, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, .periods_max = 1024, .fifo_size = 0,};/* capture on channel B */static struct snd_pcm_hardware snd_cmipci_capture ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, .rate_min = 5512, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, .periods_max = 1024, .fifo_size = 0,};/* playback on channel B - stereo 16bit only? */static struct snd_pcm_hardware snd_cmipci_playback2 ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, .rate_min = 5512, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, .periods_max = 1024, .fifo_size = 0,};/* spdif playback on channel A */static struct snd_pcm_hardware snd_cmipci_playback_spdif ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, .periods_max = 1024, .fifo_size = 0,};/* spdif playback on channel A (32bit, IEC958 subframes) */static struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, .periods_max = 1024, .fifo_size = 0,};/* spdif capture on channel B */static struct snd_pcm_hardware snd_cmipci_capture_spdif ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, .periods_max = 1024, .fifo_size = 0,};/* * check device open/close */static int open_device_check(struct cmipci *cm, int mode, struct snd_pcm_substream *subs){ int ch = mode & CM_OPEN_CH_MASK; /* FIXME: a file should wait until the device becomes free * when it's opened on blocking mode. however, since the current * pcm framework doesn't pass file pointer before actually opened, * we can't know whether blocking mode or not in open callback.. */ mutex_lock(&cm->open_mutex); if (cm->opened[ch]) { mutex_unlock(&cm->open_mutex); return -EBUSY; } cm->opened[ch] = mode; cm->channel[ch].substream = subs; if (! (mode & CM_OPEN_DAC)) { /* disable dual DAC mode */ cm->channel[ch].is_dac = 0; spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); spin_unlock_irq(&cm->reg_lock); } mutex_unlock(&cm->open_mutex); return 0;}static void close_device_check(struct cmipci *cm, int mode){ int ch = mode & CM_OPEN_CH_MASK; mutex_lock(&cm->open_mutex); if (cm->opened[ch] == mode) { if (cm->channel[ch].substream) { snd_cmipci_ch_reset(cm, ch); cm->channel[ch].running = 0; cm->channel[ch].substream = NULL; } cm->opened[ch] = 0; if (! cm->channel[ch].is_dac) { /* enable dual DAC mode again */ cm->channel[ch].is_dac = 1; spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); spin_unlock_irq(&cm->reg_lock); } } mutex_unlock(&cm->open_mutex);}/* */static int snd_cmipci_playback_open(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0) return err; runtime->hw = snd_cmipci_playback; if (cm->chip_version == 68) { runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); cm->dig_pcm_status = cm->dig_status; return 0;}static int snd_cmipci_capture_open(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0) return err; runtime->hw = snd_cmipci_capture; if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording runtime->hw.rate_min = 41000; runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); return 0;}static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream){ struct cmipci *cm = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */ return err; runtime->hw = snd_cmipci_playback2; mutex_lock(&cm->open_mutex); if (! cm->opened[CM_CH_PLAY]) { if (cm->can_multi_ch) { runtime->hw.channels_max = cm->max_channels; if (cm->max_channels == 4) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4); else if (cm->max_channels == 6) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6); else if (cm->max_channels == 8) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_8); } } mutex_unlock(&cm->open_mutex); if (cm->chip_version == 68) { runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -