📄 es1968.c
字号:
/* playback/capture pcm buffer */ struct esm_memory *memory; /* capture mixer buffer */ struct esm_memory *mixbuf; unsigned int hwptr; /* current hw pointer in bytes */ unsigned int count; /* sample counter in bytes */ unsigned int dma_size; /* total buffer size in bytes */ unsigned int frag_size; /* period size in bytes */ unsigned int wav_shift; u16 base[4]; /* offset for ptr */ /* stereo/16bit flag */ unsigned char fmt; int mode; /* playback / capture */ int bob_freq; /* required timer frequency */ struct snd_pcm_substream *substream; /* linked list */ struct list_head list;#ifdef CONFIG_PM u16 wc_map[4];#endif};struct es1968 { /* Module Config */ int total_bufsize; /* in bytes */ int playback_streams, capture_streams; unsigned int clock; /* clock */ /* for clock measurement */ unsigned int in_measurement: 1; unsigned int measure_apu; unsigned int measure_lastpos; unsigned int measure_count; /* buffer */ struct snd_dma_buffer dma; /* Resources... */ int irq; unsigned long io_port; int type; struct pci_dev *pci; struct snd_card *card; struct snd_pcm *pcm; int do_pm; /* power-management enabled */ /* DMA memory block */ struct list_head buf_list; /* ALSA Stuff */ struct snd_ac97 *ac97; struct snd_kcontrol *master_switch; /* for h/w volume control */ struct snd_kcontrol *master_volume; struct snd_rawmidi *rmidi; spinlock_t reg_lock; spinlock_t ac97_lock; struct tasklet_struct hwvol_tq; unsigned int in_suspend; /* Maestro Stuff */ u16 maestro_map[32]; int bobclient; /* active timer instancs */ int bob_freq; /* timer frequency */ struct mutex memory_mutex; /* memory lock */ /* APU states */ unsigned char apu[NR_APUS]; /* active substreams */ struct list_head substream_list; spinlock_t substream_lock;#ifdef CONFIG_PM u16 apu_map[NR_APUS][NR_APU_REGS];#endif#ifdef SUPPORT_JOYSTICK struct gameport *gameport;#endif};static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);static struct pci_device_id snd_es1968_ids[] = { /* Maestro 1 */ { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO }, /* Maestro 2 */ { 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2 }, /* Maestro 2E */ { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2E }, { 0, }};MODULE_DEVICE_TABLE(pci, snd_es1968_ids);/* ********************* * Low Level Funcs! * *********************//* no spinlock */static void __maestro_write(struct es1968 *chip, u16 reg, u16 data){ outw(reg, chip->io_port + ESM_INDEX); outw(data, chip->io_port + ESM_DATA); chip->maestro_map[reg] = data;}static inline void maestro_write(struct es1968 *chip, u16 reg, u16 data){ unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); __maestro_write(chip, reg, data); spin_unlock_irqrestore(&chip->reg_lock, flags);}/* no spinlock */static u16 __maestro_read(struct es1968 *chip, u16 reg){ if (READABLE_MAP & (1 << reg)) { outw(reg, chip->io_port + ESM_INDEX); chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA); } return chip->maestro_map[reg];}static inline u16 maestro_read(struct es1968 *chip, u16 reg){ unsigned long flags; u16 result; spin_lock_irqsave(&chip->reg_lock, flags); result = __maestro_read(chip, reg); spin_unlock_irqrestore(&chip->reg_lock, flags); return result;}/* Wait for the codec bus to be free */static int snd_es1968_ac97_wait(struct es1968 *chip){ int timeout = 100000; while (timeout-- > 0) { if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1)) return 0; cond_resched(); } snd_printd("es1968: ac97 timeout\n"); return 1; /* timeout */}static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ struct es1968 *chip = ac97->private_data; unsigned long flags; snd_es1968_ac97_wait(chip); /* Write the bus */ spin_lock_irqsave(&chip->ac97_lock, flags); outw(val, chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ outb(reg, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ spin_unlock_irqrestore(&chip->ac97_lock, flags);}static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg){ u16 data = 0; struct es1968 *chip = ac97->private_data; unsigned long flags; snd_es1968_ac97_wait(chip); spin_lock_irqsave(&chip->ac97_lock, flags); outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ if (! snd_es1968_ac97_wait(chip)) { data = inw(chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ } spin_unlock_irqrestore(&chip->ac97_lock, flags); return data;}/* no spinlock */static void apu_index_set(struct es1968 *chip, u16 index){ int i; __maestro_write(chip, IDR1_CRAM_POINTER, index); for (i = 0; i < 1000; i++) if (__maestro_read(chip, IDR1_CRAM_POINTER) == index) return; snd_printd("es1968: APU register select failed. (Timeout)\n");}/* no spinlock */static void apu_data_set(struct es1968 *chip, u16 data){ int i; for (i = 0; i < 1000; i++) { if (__maestro_read(chip, IDR0_DATA_PORT) == data) return; __maestro_write(chip, IDR0_DATA_PORT, data); } snd_printd("es1968: APU register set probably failed (Timeout)!\n");}/* no spinlock */static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data){ snd_assert(channel < NR_APUS, return);#ifdef CONFIG_PM chip->apu_map[channel][reg] = data;#endif reg |= (channel << 4); apu_index_set(chip, reg); apu_data_set(chip, data);}static void apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data){ unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); __apu_set_register(chip, channel, reg, data); spin_unlock_irqrestore(&chip->reg_lock, flags);}static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg){ snd_assert(channel < NR_APUS, return 0); reg |= (channel << 4); apu_index_set(chip, reg); return __maestro_read(chip, IDR0_DATA_PORT);}static u16 apu_get_register(struct es1968 *chip, u16 channel, u8 reg){ unsigned long flags; u16 v; spin_lock_irqsave(&chip->reg_lock, flags); v = __apu_get_register(chip, channel, reg); spin_unlock_irqrestore(&chip->reg_lock, flags); return v;}#if 0 /* ASSP is not supported */static void assp_set_register(struct es1968 *chip, u32 reg, u32 value){ unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); outl(reg, chip->io_port + ASSP_INDEX); outl(value, chip->io_port + ASSP_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags);}static u32 assp_get_register(struct es1968 *chip, u32 reg){ unsigned long flags; u32 value; spin_lock_irqsave(&chip->reg_lock, flags); outl(reg, chip->io_port + ASSP_INDEX); value = inl(chip->io_port + ASSP_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags); return value;}#endifstatic void wave_set_register(struct es1968 *chip, u16 reg, u16 value){ unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); outw(reg, chip->io_port + WC_INDEX); outw(value, chip->io_port + WC_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags);}static u16 wave_get_register(struct es1968 *chip, u16 reg){ unsigned long flags; u16 value; spin_lock_irqsave(&chip->reg_lock, flags); outw(reg, chip->io_port + WC_INDEX); value = inw(chip->io_port + WC_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags); return value;}/* ******************* * Bob the Timer! * *******************/static void snd_es1968_bob_stop(struct es1968 *chip){ u16 reg; reg = __maestro_read(chip, 0x11); reg &= ~ESM_BOB_ENABLE; __maestro_write(chip, 0x11, reg); reg = __maestro_read(chip, 0x17); reg &= ~ESM_BOB_START; __maestro_write(chip, 0x17, reg);}static void snd_es1968_bob_start(struct es1968 *chip){ int prescale; int divide; /* compute ideal interrupt frequency for buffer size & play rate */ /* first, find best prescaler value to match freq */ for (prescale = 5; prescale < 12; prescale++) if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9))) break; /* next, back off prescaler whilst getting divider into optimum range */ divide = 1; while ((prescale > 5) && (divide < 32)) { prescale--; divide <<= 1; } divide >>= 1; /* now fine-tune the divider for best match */ for (; divide < 31; divide++) if (chip->bob_freq > ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break; /* divide = 0 is illegal, but don't let prescale = 4! */ if (divide == 0) { divide++; if (prescale > 5) prescale--; } else if (divide > 1) divide--; __maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide); /* set reg */ /* Now set IDR 11/17 */ __maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1); __maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1);}/* call with substream spinlock */static void snd_es1968_bob_inc(struct es1968 *chip, int freq){ chip->bobclient++; if (chip->bobclient == 1) { chip->bob_freq = freq; snd_es1968_bob_start(chip); } else if (chip->bob_freq < freq) { snd_es1968_bob_stop(chip); chip->bob_freq = freq; snd_es1968_bob_start(chip); }}/* call with substream spinlock */static void snd_es1968_bob_dec(struct es1968 *chip){ chip->bobclient--; if (chip->bobclient <= 0) snd_es1968_bob_stop(chip); else if (chip->bob_freq > ESM_BOB_FREQ) { /* check reduction of timer frequency */ int max_freq = ESM_BOB_FREQ; struct esschan *es; list_for_each_entry(es, &chip->substream_list, list) { if (max_freq < es->bob_freq) max_freq = es->bob_freq; } if (max_freq != chip->bob_freq) { snd_es1968_bob_stop(chip); chip->bob_freq = max_freq; snd_es1968_bob_start(chip); } }}static intsnd_es1968_calc_bob_rate(struct es1968 *chip, struct esschan *es, struct snd_pcm_runtime *runtime){ /* we acquire 4 interrupts per period for precise control.. */ int freq = runtime->rate * 4; if (es->fmt & ESS_FMT_STEREO) freq <<= 1; if (es->fmt & ESS_FMT_16BIT) freq <<= 1; freq /= es->frag_size; if (freq < ESM_BOB_FREQ) freq = ESM_BOB_FREQ; else if (freq > ESM_BOB_FREQ_MAX) freq = ESM_BOB_FREQ_MAX; return freq;}/************* * PCM Part * *************/static u32 snd_es1968_compute_rate(struct es1968 *chip, u32 freq){ u32 rate = (freq << 16) / chip->clock;#if 0 /* XXX: do we need this? */ if (rate > 0x10000) rate = 0x10000;#endif return rate;}/* get current pointer */static inline unsigned intsnd_es1968_get_dma_ptr(struct es1968 *chip, struct esschan *es){ unsigned int offset; offset = apu_get_register(chip, es->apu[0], 5); offset -= es->base[0]; return (offset & 0xFFFE); /* hardware is in words */}static void snd_es1968_apu_set_freq(struct es1968 *chip, int apu, int freq){ apu_set_register(chip, apu, 2, (apu_get_register(chip, apu, 2) & 0x00FF) | ((freq & 0xff) << 8) | 0x10); apu_set_register(chip, apu, 3, freq >> 8);}/* spin lock held */static inline void snd_es1968_trigger_apu(struct es1968 *esm, int apu, int mode){ /* set the APU mode */ __apu_set_register(esm, apu, 0, (__apu_get_register(esm, apu, 0) & 0xff0f) | (mode << 4));}static void snd_es1968_pcm_start(struct es1968 *chip, struct esschan *es){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -