📄 cmipci.c
字号:
*/static snd_pcm_uframes_t snd_cmipci_pcm_pointer(cmipci_t *cm, cmipci_pcm_t *rec, snd_pcm_substream_t *substream){ size_t ptr; unsigned int reg; if (!rec->running) return 0;#if 1 // this seems better.. reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1); ptr >>= rec->shift;#else reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; ptr = snd_cmipci_read(cm, reg) - rec->offset; ptr = bytes_to_frames(substream->runtime, ptr);#endif if (substream->runtime->channels > 2) ptr = (ptr * 2) / substream->runtime->channels; return ptr;}/* * playback */static int snd_cmipci_playback_trigger(snd_pcm_substream_t *substream, int cmd){ cmipci_t *cm = snd_pcm_substream_chip(substream); return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], substream, cmd);}static snd_pcm_uframes_t snd_cmipci_playback_pointer(snd_pcm_substream_t *substream){ cmipci_t *cm = snd_pcm_substream_chip(substream); return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream);}/* * capture */static int snd_cmipci_capture_trigger(snd_pcm_substream_t *substream, int cmd){ cmipci_t *cm = snd_pcm_substream_chip(substream); return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], substream, cmd);}static snd_pcm_uframes_t snd_cmipci_capture_pointer(snd_pcm_substream_t *substream){ cmipci_t *cm = snd_pcm_substream_chip(substream); return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream);}/* * hw preparation for spdif */static int snd_cmipci_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0;}static int snd_cmipci_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ cmipci_t *chip = snd_kcontrol_chip(kcontrol); int i; spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff; spin_unlock_irq(&chip->reg_lock); return 0;}static int snd_cmipci_spdif_default_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ cmipci_t *chip = snd_kcontrol_chip(kcontrol); int i, change; unsigned int val; val = 0; spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); change = val != chip->dig_status; chip->dig_status = val; spin_unlock_irq(&chip->reg_lock); return change;}static snd_kcontrol_new_t snd_cmipci_spdif_default __devinitdata ={ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .info = snd_cmipci_spdif_default_info, .get = snd_cmipci_spdif_default_get, .put = snd_cmipci_spdif_default_put};static int snd_cmipci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0;}static int snd_cmipci_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol){ ucontrol->value.iec958.status[0] = 0xff; ucontrol->value.iec958.status[1] = 0xff; ucontrol->value.iec958.status[2] = 0xff; ucontrol->value.iec958.status[3] = 0xff; return 0;}static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata ={ .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_cmipci_spdif_mask_info, .get = snd_cmipci_spdif_mask_get,};static int snd_cmipci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0;}static int snd_cmipci_spdif_stream_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ cmipci_t *chip = snd_kcontrol_chip(kcontrol); int i; spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff; spin_unlock_irq(&chip->reg_lock); return 0;}static int snd_cmipci_spdif_stream_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ cmipci_t *chip = snd_kcontrol_chip(kcontrol); int i, change; unsigned int val; val = 0; spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); change = val != chip->dig_pcm_status; chip->dig_pcm_status = val; spin_unlock_irq(&chip->reg_lock); return change;}static snd_kcontrol_new_t snd_cmipci_spdif_stream __devinitdata ={ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), .info = snd_cmipci_spdif_stream_info, .get = snd_cmipci_spdif_stream_get, .put = snd_cmipci_spdif_stream_put};/* *//* save mixer setting and mute for AC3 playback */static int save_mixer_state(cmipci_t *cm){ if (! cm->mixer_insensitive) { snd_ctl_elem_value_t *val; unsigned int i; val = kmalloc(sizeof(*val), GFP_ATOMIC); if (!val) return -ENOMEM; for (i = 0; i < CM_SAVED_MIXERS; i++) { snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; if (ctl) { int event; memset(val, 0, sizeof(*val)); ctl->get(ctl, val); cm->mixer_res_status[i] = val->value.integer.value[0]; val->value.integer.value[0] = cm_saved_mixer[i].toggle_on; event = SNDRV_CTL_EVENT_MASK_INFO; if (cm->mixer_res_status[i] != val->value.integer.value[0]) { ctl->put(ctl, val); /* toggle */ event |= SNDRV_CTL_EVENT_MASK_VALUE; } ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(cm->card, event, &ctl->id); } } kfree(val); cm->mixer_insensitive = 1; } return 0;}/* restore the previously saved mixer status */static void restore_mixer_state(cmipci_t *cm){ if (cm->mixer_insensitive) { snd_ctl_elem_value_t *val; unsigned int i; val = kmalloc(sizeof(*val), GFP_KERNEL); if (!val) return; cm->mixer_insensitive = 0; /* at first clear this; otherwise the changes will be ignored */ for (i = 0; i < CM_SAVED_MIXERS; i++) { snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; if (ctl) { int event; memset(val, 0, sizeof(*val)); ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; ctl->get(ctl, val); event = SNDRV_CTL_EVENT_MASK_INFO; if (val->value.integer.value[0] != cm->mixer_res_status[i]) { val->value.integer.value[0] = cm->mixer_res_status[i]; ctl->put(ctl, val); event |= SNDRV_CTL_EVENT_MASK_VALUE; } snd_ctl_notify(cm->card, event, &ctl->id); } } kfree(val); }}/* spinlock held! */static void setup_ac3(cmipci_t *cm, snd_pcm_substream_t *subs, int do_ac3, int rate){ if (do_ac3) { /* AC3EN for 037 */ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); /* AC3EN for 039 */ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); if (cm->can_ac3_hw) { /* SPD24SEL for 037, 0x02 */ /* SPD24SEL for 039, 0x20, but cannot be set */ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); } else { /* can_ac3_sw */ /* SPD32SEL for 037 & 039, 0x20 */ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); /* set 176K sample rate to fix 033 HW bug */ if (cm->chip_version == 33) { if (rate >= 48000) { snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); } else { snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); } } } } else { snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); if (cm->can_ac3_hw) { /* chip model >= 37 */ if (snd_pcm_format_width(subs->runtime->format) > 16) { snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); } else { snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); } } else { snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); } }}static int setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3){ int rate, err; rate = subs->runtime->rate; if (up && do_ac3) if ((err = save_mixer_state(cm)) < 0) return err; spin_lock_irq(&cm->reg_lock); cm->spdif_playback_avail = up; if (up) { /* they are controlled via "IEC958 Output Switch" */ /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ if (cm->spdif_playback_enabled) snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); setup_ac3(cm, subs, do_ac3, rate); if (rate == 48000) snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); else snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); } else { /* they are controlled via "IEC958 Output Switch" */ /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); setup_ac3(cm, subs, 0, 0); } spin_unlock_irq(&cm->reg_lock); return 0;}/* * preparation *//* playback - enable spdif only on the certain condition */static int snd_cmipci_playback_prepare(snd_pcm_substream_t *substream){ cmipci_t *cm = snd_pcm_substream_chip(substream); int rate = substream->runtime->rate; int err, do_spdif, do_ac3 = 0; do_spdif = ((rate == 44100 || rate == 48000) && 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(snd_pcm_substream_t *substream){ cmipci_t *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);}static int snd_cmipci_playback_hw_free(snd_pcm_substream_t *substream){ cmipci_t *cm = snd_pcm_substream_chip(substream); setup_spdif_playback(cm, substream, 0, 0); restore_mixer_state(cm); return snd_cmipci_hw_free(substream);}/* capture */static int snd_cmipci_capture_prepare(snd_pcm_substream_t *substream){ cmipci_t *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(snd_pcm_substream_t *substream){ cmipci_t *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(snd_pcm_substream_t *subs){ cmipci_t *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 pt_regs *regs){ cmipci_t *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -