📄 cmipci.c
字号:
unsigned long iobase; unsigned int ctrl; /* FUNCTRL0 current value */ snd_pcm_t *pcm; /* DAC/ADC PCM */ snd_pcm_t *pcm2; /* 2nd DAC */ snd_pcm_t *pcm_spdif; /* SPDIF */ int chip_version; int max_channels; unsigned int has_dual_dac: 1; unsigned int can_ac3_sw: 1; unsigned int can_ac3_hw: 1; unsigned int can_multi_ch: 1; unsigned int do_soft_ac3: 1; unsigned int spdif_playback_avail: 1; /* spdif ready? */ unsigned int spdif_playback_enabled: 1; /* spdif switch enabled? */ int spdif_counter; /* for software AC3 */ unsigned int dig_status; unsigned int dig_pcm_status; snd_pcm_hardware_t *hw_info[3]; /* for playbacks */ int opened[2]; /* open mode */ struct semaphore open_mutex; int mixer_insensitive: 1; snd_kcontrol_t *mixer_res_ctl[CM_SAVED_MIXERS]; int mixer_res_status[CM_SAVED_MIXERS]; opl3_t *opl3; snd_hwdep_t *opl3hwdep; cmipci_pcm_t channel[2]; /* ch0 - DAC, ch1 - ADC or 2nd DAC */ /* external MIDI */ snd_rawmidi_t *rmidi;#ifdef SUPPORT_JOYSTICK struct gameport gameport; struct resource *res_joystick;#endif spinlock_t reg_lock;};/* read/write operations for dword register */inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data){ outl(data, cm->iobase + cmd);}inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd){ return inl(cm->iobase + cmd);}/* read/write operations for word register */inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data){ outw(data, cm->iobase + cmd);}inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd){ return inw(cm->iobase + cmd);}/* read/write operations for byte register */inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data){ outb(data, cm->iobase + cmd);}inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd){ return inb(cm->iobase + cmd);}/* bit operations for dword register */static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag){ unsigned int val; val = inl(cm->iobase + cmd); val |= flag; outl(val, cm->iobase + cmd);}static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag){ unsigned int val; val = inl(cm->iobase + cmd); val &= ~flag; outl(val, cm->iobase + cmd);}#if 0 // not used/* bit operations for byte register */static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag){ unsigned char val; val = inb(cm->iobase + cmd); val |= flag; outb(val, cm->iobase + cmd);}static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag){ unsigned char val; val = inb(cm->iobase + cmd); val &= ~flag; outb(val, cm->iobase + cmd);}#endif/* * PCM interface *//* * calculate frequency */static unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };static unsigned int snd_cmipci_rate_freq(unsigned int rate){ unsigned int i; for (i = 0; i < ARRAY_SIZE(rates); i++) { if (rates[i] == rate) return i; } snd_BUG(); return 0;}#ifdef USE_VAR48KRATE/* * Determine PLL values for frequency setup, maybe the CMI8338 (CMI8738???) * does it this way .. maybe not. Never get any information from C-Media about * that <werner@suse.de>. */static int snd_cmipci_pll_rmn(unsigned int rate, unsigned int adcmult, int *r, int *m, int *n){ unsigned int delta, tolerance; int xm, xn, xr; for (*r = 0; rate < CM_MAXIMUM_RATE/adcmult; *r += (1<<5)) rate <<= 1; *n = -1; if (*r > 0xff) goto out; tolerance = rate*CM_TOLERANCE_RATE; for (xn = (1+2); xn < (0x1f+2); xn++) { for (xm = (1+2); xm < (0xff+2); xm++) { xr = ((CM_REFFREQ_XIN/adcmult) * xm) / xn; if (xr < rate) delta = rate - xr; else delta = xr - rate; /* * If we found one, remember this, * and try to find a closer one */ if (delta < tolerance) { tolerance = delta; *m = xm - 2; *n = xn - 2; } } }out: return (*n > -1);}/* * Program pll register bits, I assume that the 8 registers 0xf8 upto 0xff * are mapped onto the 8 ADC/DAC sampling frequency which can be choosen * at the register CM_REG_FUNCTRL1 (0x04). * Problem: other ways are also possible (any information about that?) */static void snd_cmipci_set_pll(cmipci_t *cm, unsigned int rate, unsigned int slot){ unsigned int reg = CM_REG_PLL + slot; /* * Guess that this programs at reg. 0x04 the pos 15:13/12:10 * for DSFC/ASFC (000 upto 111). */ /* FIXME: Init (Do we've to set an other register first before programming?) */ /* FIXME: Is this correct? Or shouldn't the m/n/r values be used for that? */ snd_cmipci_write_b(cm, reg, rate>>8); snd_cmipci_write_b(cm, reg, rate&0xff); /* FIXME: Setup (Do we've to set an other register first to enable this?) */}#endif /* USE_VAR48KRATE */static int snd_cmipci_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_cmipci_playback2_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ cmipci_t *cm = snd_pcm_substream_chip(substream); if (params_channels(hw_params) > 2) { down(&cm->open_mutex); if (cm->opened[CM_CH_PLAY]) { up(&cm->open_mutex); return -EBUSY; } /* reserve the channel A */ cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI; up(&cm->open_mutex); } return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static void snd_cmipci_ch_reset(cmipci_t *cm, int ch){ int reset = CM_RST_CH0 << (cm->channel[ch].ch); snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset); udelay(10);}static int snd_cmipci_hw_free(snd_pcm_substream_t * substream){ return snd_pcm_lib_free_pages(substream);}/* */static unsigned int hw_channels[] = {1, 2, 4, 5, 6};static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = { .count = 3, .list = hw_channels, .mask = 0,};static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = { .count = 5, .list = hw_channels, .mask = 0,};static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels){ if (channels > 2) { if (! cm->can_multi_ch) return -EINVAL; if (rec->fmt != 0x03) /* stereo 16bit only */ return -EINVAL; spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); if (channels > 4) { snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); } else { snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); } if (channels == 6) { snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); } else { snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); } spin_unlock_irq(&cm->reg_lock); } else { if (cm->can_multi_ch) { spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); spin_unlock_irq(&cm->reg_lock); } } return 0;}/* * prepare playback/capture channel * channel to be used must have been set in rec->ch. */static int snd_cmipci_pcm_prepare(cmipci_t *cm, cmipci_pcm_t *rec, snd_pcm_substream_t *substream){ unsigned int reg, freq, val; snd_pcm_runtime_t *runtime = substream->runtime; rec->fmt = 0; rec->shift = 0; if (snd_pcm_format_width(runtime->format) >= 16) { rec->fmt |= 0x02; if (snd_pcm_format_width(runtime->format) > 16) rec->shift++; /* 24/32bit */ } if (runtime->channels > 1) rec->fmt |= 0x01; if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) { snd_printd("cannot set dac channels\n"); return -EINVAL; } rec->offset = runtime->dma_addr; /* buffer and period sizes in frame */ rec->dma_size = runtime->buffer_size << rec->shift; rec->period_size = runtime->period_size << rec->shift; if (runtime->channels > 2) { /* multi-channels */ rec->dma_size = (rec->dma_size * runtime->channels) / 2; rec->period_size = (rec->period_size * runtime->channels) / 2; } spin_lock_irq(&cm->reg_lock); /* set buffer address */ reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; snd_cmipci_write(cm, reg, rec->offset); /* program sample counts */ reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; snd_cmipci_write_w(cm, reg, rec->dma_size - 1); snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1); /* set adc/dac flag */ val = rec->ch ? CM_CHADC1 : CM_CHADC0; if (rec->is_dac) cm->ctrl &= ~val; else cm->ctrl |= val; snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); /* set sample rate */ freq = snd_cmipci_rate_freq(runtime->rate); val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); if (rec->ch) { val &= ~CM_ASFC_MASK; val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK; } else { val &= ~CM_DSFC_MASK; val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK; } snd_cmipci_write(cm, CM_REG_FUNCTRL1, val); //snd_printd("cmipci: functrl1 = %08x\n", val); /* set format */ val = snd_cmipci_read(cm, CM_REG_CHFORMAT); if (rec->ch) { val &= ~CM_CH1FMT_MASK; val |= rec->fmt << CM_CH1FMT_SHIFT; } else { val &= ~CM_CH0FMT_MASK; val |= rec->fmt << CM_CH0FMT_SHIFT; } snd_cmipci_write(cm, CM_REG_CHFORMAT, val); //snd_printd("cmipci: chformat = %08x\n", val); rec->running = 0; spin_unlock_irq(&cm->reg_lock); return 0;}/* * PCM trigger/stop */static int snd_cmipci_pcm_trigger(cmipci_t *cm, cmipci_pcm_t *rec, snd_pcm_substream_t *substream, int cmd){ unsigned int inthld, chen, reset, pause; int result = 0; inthld = CM_CH0_INT_EN << rec->ch; chen = CM_CHEN0 << rec->ch; reset = CM_RST_CH0 << rec->ch; pause = CM_PAUSE0 << rec->ch; spin_lock(&cm->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: rec->running = 1; /* set interrupt */ snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld); cm->ctrl |= chen; /* enable channel */ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); break; case SNDRV_PCM_TRIGGER_STOP: rec->running = 0; /* disable interrupt */ snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld); /* reset */ cm->ctrl &= ~chen; snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: cm->ctrl |= pause; snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: cm->ctrl &= ~pause; snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); break; default: result = -EINVAL; break; } spin_unlock(&cm->reg_lock); return result;}/* * return the current pointer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -