📄 cmipci.c
字号:
#endif/* * driver data */struct cmipci_pcm { struct snd_pcm_substream *substream; u8 running; /* dac/adc running? */ u8 fmt; /* format bits */ u8 is_dac; u8 needs_silencing; unsigned int dma_size; /* in frames */ unsigned int shift; unsigned int ch; /* channel (0/1) */ unsigned int offset; /* physical address of the buffer */};/* mixer elements toggled/resumed during ac3 playback */struct cmipci_mixer_auto_switches { const char *name; /* switch to toggle */ int toggle_on; /* value to change when ac3 mode */};static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = { {"PCM Playback Switch", 0}, {"IEC958 Output Switch", 1}, {"IEC958 Mix Analog", 0}, // {"IEC958 Out To DAC", 1}, // no longer used {"IEC958 Loop", 0},};#define CM_SAVED_MIXERS ARRAY_SIZE(cm_saved_mixer)struct cmipci { struct snd_card *card; struct pci_dev *pci; unsigned int device; /* device ID */ int irq; unsigned long iobase; unsigned int ctrl; /* FUNCTRL0 current value */ struct snd_pcm *pcm; /* DAC/ADC PCM */ struct snd_pcm *pcm2; /* 2nd DAC */ struct snd_pcm *pcm_spdif; /* SPDIF */ int chip_version; int max_channels; 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; struct snd_pcm_hardware *hw_info[3]; /* for playbacks */ int opened[2]; /* open mode */ struct mutex open_mutex; unsigned int mixer_insensitive: 1; struct snd_kcontrol *mixer_res_ctl[CM_SAVED_MIXERS]; int mixer_res_status[CM_SAVED_MIXERS]; struct cmipci_pcm channel[2]; /* ch0 - DAC, ch1 - ADC or 2nd DAC */ /* external MIDI */ struct snd_rawmidi *rmidi;#ifdef SUPPORT_JOYSTICK struct gameport *gameport;#endif spinlock_t reg_lock;#ifdef CONFIG_PM unsigned int saved_regs[0x20]; unsigned char saved_mixers[0x20];#endif};/* read/write operations for dword register */static inline void snd_cmipci_write(struct cmipci *cm, unsigned int cmd, unsigned int data){ outl(data, cm->iobase + cmd);}static inline unsigned int snd_cmipci_read(struct cmipci *cm, unsigned int cmd){ return inl(cm->iobase + cmd);}/* read/write operations for word register */static inline void snd_cmipci_write_w(struct cmipci *cm, unsigned int cmd, unsigned short data){ outw(data, cm->iobase + cmd);}static inline unsigned short snd_cmipci_read_w(struct cmipci *cm, unsigned int cmd){ return inw(cm->iobase + cmd);}/* read/write operations for byte register */static inline void snd_cmipci_write_b(struct cmipci *cm, unsigned int cmd, unsigned char data){ outb(data, cm->iobase + cmd);}static inline unsigned char snd_cmipci_read_b(struct cmipci *cm, unsigned int cmd){ return inb(cm->iobase + cmd);}/* bit operations for dword register */static int snd_cmipci_set_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag){ unsigned int val, oval; val = oval = inl(cm->iobase + cmd); val |= flag; if (val == oval) return 0; outl(val, cm->iobase + cmd); return 1;}static int snd_cmipci_clear_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag){ unsigned int val, oval; val = oval = inl(cm->iobase + cmd); val &= ~flag; if (val == oval) return 0; outl(val, cm->iobase + cmd); return 1;}/* bit operations for byte register */static int snd_cmipci_set_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag){ unsigned char val, oval; val = oval = inb(cm->iobase + cmd); val |= flag; if (val == oval) return 0; outb(val, cm->iobase + cmd); return 1;}static int snd_cmipci_clear_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag){ unsigned char val, oval; val = oval = inb(cm->iobase + cmd); val &= ~flag; if (val == oval) return 0; outb(val, cm->iobase + cmd); return 1;}/* * 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; if (rate > 48000) rate /= 2; 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(struct cmipci *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(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_cmipci_playback2_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct cmipci *cm = snd_pcm_substream_chip(substream); if (params_channels(hw_params) > 2) { mutex_lock(&cm->open_mutex); if (cm->opened[CM_CH_PLAY]) { mutex_unlock(&cm->open_mutex); return -EBUSY; } /* reserve the channel A */ cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI; mutex_unlock(&cm->open_mutex); } return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static void snd_cmipci_ch_reset(struct cmipci *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(struct snd_pcm_substream *substream){ return snd_pcm_lib_free_pages(substream);}/* */static unsigned int hw_channels[] = {1, 2, 4, 6, 8};static struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = { .count = 3, .list = hw_channels, .mask = 0,};static struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = { .count = 4, .list = hw_channels, .mask = 0,};static struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = { .count = 5, .list = hw_channels, .mask = 0,};static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int channels){ if (channels > 2) { if (!cm->can_multi_ch || !rec->ch) return -EINVAL; if (rec->fmt != 0x03) /* stereo 16bit only */ return -EINVAL; } if (cm->can_multi_ch) { spin_lock_irq(&cm->reg_lock); if (channels > 2) { snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); } else { snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); } if (channels == 8) snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_CHB3D8C); else snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_CHB3D8C); if (channels == 6) { snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); } else { snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); } if (channels == 4) snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); else snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); 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(struct cmipci *cm, struct cmipci_pcm *rec, struct snd_pcm_substream *substream){ unsigned int reg, freq, val; unsigned int period_size; struct snd_pcm_runtime *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; period_size = runtime->period_size << rec->shift; if (runtime->channels > 2) { /* multi-channels */ rec->dma_size = (rec->dma_size * runtime->channels) / 2; period_size = (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, 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_DSFC_MASK; val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK; } else { val &= ~CM_ASFC_MASK; val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -