📄 cs4231_lib.c
字号:
static unsigned char snd_cs4231_get_rate(unsigned int rate){ int i; for (i = 0; i < 14; i++) if (rate == rates[i]) return freq_bits[i]; // snd_BUG(); return freq_bits[13];}static unsigned char snd_cs4231_get_format(cs4231_t *chip, int format, int channels){ unsigned char rformat; rformat = CS4231_LINEAR_8; switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break; case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break; case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break; case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break; case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break; } if (channels > 1) rformat |= CS4231_STEREO;#if 0 snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);#endif return rformat;}static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute){ unsigned long flags; mute = mute ? 1 : 0; spin_lock_irqsave(&chip->reg_lock, flags); if (chip->calibrate_mute == mute) { spin_unlock_irqrestore(&chip->reg_lock, flags); return; } if (!mute) { snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]); snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]); snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]); } snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]); snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]); snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]); snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]); snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]); snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]); snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]); snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]); snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); if (chip->hardware == CS4231_HW_INTERWAVE) { snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]); snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]); snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]); snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]); } chip->calibrate_mute = mute; spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_cs4231_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr){ unsigned long flags; int full_calib = 1; down(&chip->mce_mutex); snd_cs4231_calibrate_mute(chip, 1); if (chip->hardware == CS4231_HW_CS4231A || (chip->hardware & CS4231_HW_CS4232_MASK)) { spin_lock_irqsave(&chip->reg_lock, flags); if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */ snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10); udelay(100); /* Fixes audible clicks at least on GUS MAX */ full_calib = 0; } spin_unlock_irqrestore(&chip->reg_lock, flags); } if (full_calib) { snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) { snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ? (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) : pdfr); } else { snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); } spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip); } snd_cs4231_calibrate_mute(chip, 0); up(&chip->mce_mutex);}static void snd_cs4231_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr){ unsigned long flags; int full_calib = 1; down(&chip->mce_mutex); snd_cs4231_calibrate_mute(chip, 1); if (chip->hardware == CS4231_HW_CS4231A || (chip->hardware & CS4231_HW_CS4232_MASK)) { spin_lock_irqsave(&chip->reg_lock, flags); if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */ (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr); snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20); full_calib = 0; } spin_unlock_irqrestore(&chip->reg_lock, flags); } if (full_calib) { snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); if (chip->hardware != CS4231_HW_INTERWAVE) { if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) | (cdfr & 0x0f)); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip); snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); } } snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip); } snd_cs4231_calibrate_mute(chip, 0); up(&chip->mce_mutex);}/* * Timer interface */static unsigned long snd_cs4231_timer_resolution(snd_timer_t * timer){ cs4231_t *chip = snd_timer_chip(timer); if (chip->hardware & CS4231_HW_CS4236B_MASK) return 14467; else return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;}static int snd_cs4231_timer_start(snd_timer_t * timer){ unsigned long flags; unsigned int ticks; cs4231_t *chip = snd_timer_chip(timer); spin_lock_irqsave(&chip->reg_lock, flags); ticks = timer->sticks; if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8)); snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks); snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE); } spin_unlock_irqrestore(&chip->reg_lock, flags); return 0;}static int snd_cs4231_timer_stop(snd_timer_t * timer){ unsigned long flags; cs4231_t *chip = snd_timer_chip(timer); spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE); spin_unlock_irqrestore(&chip->reg_lock, flags); return 0;}static void snd_cs4231_init(cs4231_t *chip){ unsigned long flags; snd_cs4231_mce_down(chip);#ifdef SNDRV_DEBUG_MCE snd_printk("init: (1)\n");#endif snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | CS4231_RECORD_ENABLE | CS4231_RECORD_PIO | CS4231_CALIB_MODE); chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip);#ifdef SNDRV_DEBUG_MCE snd_printk("init: (2)\n");#endif snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip);#ifdef SNDRV_DEBUG_MCE snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]);#endif spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip);#ifdef SNDRV_DEBUG_MCE snd_printk("init: (4)\n");#endif snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip);#ifdef SNDRV_DEBUG_MCE snd_printk("init: (5)\n");#endif}static int snd_cs4231_open(cs4231_t *chip, unsigned int mode){ unsigned long flags; down(&chip->open_mutex); if ((chip->mode & mode) || ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) { up(&chip->open_mutex); return -EAGAIN; } if (chip->mode & CS4231_MODE_OPEN) { chip->mode |= mode; up(&chip->open_mutex); return 0; } /* ok. now enable and ack CODEC IRQ */ spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | CS4231_RECORD_IRQ | CS4231_TIMER_IRQ); snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE; snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | CS4231_RECORD_IRQ | CS4231_TIMER_IRQ); snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); spin_unlock_irqrestore(&chip->reg_lock, flags); chip->mode = mode; up(&chip->open_mutex); return 0;}static void snd_cs4231_close(cs4231_t *chip, unsigned int mode){ unsigned long flags; down(&chip->open_mutex); chip->mode &= ~mode; if (chip->mode & CS4231_MODE_OPEN) { up(&chip->open_mutex); return; } snd_cs4231_calibrate_mute(chip, 1); /* disable IRQ */ spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE; snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); /* now disable record & playback */ if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip); spin_lock_irqsave(&chip->reg_lock, flags); } /* clear IRQ again */ snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_calibrate_mute(chip, 0); chip->mode = 0; up(&chip->open_mutex);}/* * timer open/close */static int snd_cs4231_timer_open(snd_timer_t * timer){ cs4231_t *chip = snd_timer_chip(timer); snd_cs4231_open(chip, CS4231_MODE_TIMER); return 0;}static int snd_cs4231_timer_close(snd_timer_t * timer){ cs4231_t *chip = snd_timer_chip(timer); snd_cs4231_close(chip, CS4231_MODE_TIMER); return 0;}static struct _snd_timer_hardware snd_cs4231_timer_table ={ .flags = SNDRV_TIMER_HW_AUTO, .resolution = 9945, .ticks = 65535, .open = snd_cs4231_timer_open, .close = snd_cs4231_timer_close, .c_resolution = snd_cs4231_timer_resolution, .start = snd_cs4231_timer_start, .stop = snd_cs4231_timer_stop,};/* * ok.. exported functions.. */static int snd_cs4231_playback_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ cs4231_t *chip = snd_pcm_substream_chip(substream); unsigned char new_pdfr; int err; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) return err; new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | snd_cs4231_get_rate(params_rate(hw_params)); chip->set_playback_format(chip, hw_params, new_pdfr); return 0;}static int snd_cs4231_playback_hw_free(snd_pcm_substream_t * substream){ return snd_pcm_lib_free_pages(substream);}#ifdef LEGACY_SUPPORTstatic int snd_cs4231_playback_prepare(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned long flags; unsigned int size = snd_pcm_lib_buffer_bytes(substream); unsigned int count = snd_pcm_lib_period_bytes(substream); spin_lock_irqsave(&chip->reg_lock, flags); chip->p_dma_size = size; chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1; snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); spin_unlock_irqrestore(&chip->reg_lock, flags);#if 0 snd_cs4231_debug(chip);#endif return 0;}#endif /* LEGACY_SUPPORT */static int snd_cs4231_capture_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ cs4231_t *chip = snd_pcm_substream_chip(substream); unsigned char new_cdfr; int err; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) return err; new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | snd_cs4231_get_rate(params_rate(hw_params)); chip->set_capture_format(chip, hw_params, new_cdfr); return 0;}static int snd_cs4231_capture_hw_free(snd_pcm_substream_t * substream){ return snd_pcm_lib_free_pages(substream);}#ifdef LEGACY_SUPPORTstatic int snd_cs4231_capture_prepare(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned long flags; unsigned int size = snd_pcm_lib_buffer_bytes(substream); unsigned int count = snd_pcm_lib_period_bytes(substream); spin_lock_irqsave(&chip->reg_lock, flags); chip->c_dma_size = size; chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1; if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); } else { snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count); snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8)); } spin_unlock_irqrestore(&chip->reg_lock, flags); return 0;}#endifstatic void snd_cs4231_overrange(cs4231_t *chip){ unsigned long flags; unsigned char res; spin_lock_irqsave(&chip->reg_lock, flags); res = snd_cs4231_in(chip, CS4231_TEST_INIT); spin_unlock_irqrestore(&chip->reg_lock, flags); if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */ chip->capture_substream->runtime->overrange++;}irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs){ cs4231_t *chip = dev_id; unsigned char status; status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); if (status & CS4231_TIMER_IRQ) { if (chip->timer) snd_timer_interrupt(chip->timer, chip->timer->sticks); } if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { if (status & CS4231_PLAYBACK_IRQ) { if (chip->mode & CS4231_MODE_PLAY) { if (chip->playback_substream) snd_pcm_period_elapsed(chip->playback_substream); } if (chip->mode & CS4231_MODE_RECORD) { if (chip->capture_substream) { snd_cs4231_overrange(chip); snd_pcm_period_elapsed(chip->capture_substream); } } } } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -