es1968.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,346 行 · 第 1/5 页
C
2,346 行
#define ACPI_SB ( 1 << 5) /* sb emul */#define ACPI_FM ( 1 << 4) /* fm emul */#define ACPI_RB ( 1 << 3) /* ringbus / aclink */#define ACPI_MIDI ( 1 << 2) #define ACPI_GP ( 1 << 1) /* game port */#define ACPI_WP ( 1 << 0) /* wave processor */#define ACPI_ALL (0xffff)#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \ ACPI_MIDI|ACPI_GP|ACPI_WP))#define ACPI_NONE (ACPI__10)/* these masks indicate which units we care about at which states */static u16 acpi_state_mask[] = { [ACPI_D0] = ACPI_ALL, [ACPI_D1] = ACPI_SLEEP, [ACPI_D2] = ACPI_SLEEP, [ACPI_D3] = ACPI_NONE};typedef struct snd_es1968 es1968_t;typedef struct snd_esschan esschan_t;typedef struct snd_esm_memory esm_memory_t;/* APU use in the driver */enum snd_enum_apu_type { ESM_APU_PCM_PLAY, ESM_APU_PCM_CAPTURE, ESM_APU_PCM_RATECONV, ESM_APU_FREE};/* chip type */enum { TYPE_MAESTRO, TYPE_MAESTRO2, TYPE_MAESTRO2E};/* DMA Hack! */struct snd_esm_memory { struct snd_dma_buffer buf; int empty; /* status */ struct list_head list;};/* Playback Channel */struct snd_esschan { int running; u8 apu[4]; u8 apu_mode[4]; /* playback/capture pcm buffer */ esm_memory_t *memory; /* capture mixer buffer */ esm_memory_t *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 */ snd_pcm_substream_t *substream; /* linked list */ struct list_head list;#ifdef CONFIG_PM u16 wc_map[4];#endif};struct snd_es1968 { /* Module Config */ int total_bufsize; /* in bytes */ int playback_streams, capture_streams; unsigned int clock; /* clock */ /* buffer */ struct snd_dma_buffer dma; /* Resources... */ int irq; unsigned long io_port; int type; struct pci_dev *pci; snd_card_t *card; snd_pcm_t *pcm; int do_pm; /* power-management enabled */ /* DMA memory block */ struct list_head buf_list; /* ALSA Stuff */ ac97_t *ac97; snd_kcontrol_t *master_switch; /* for h/w volume control */ snd_kcontrol_t *master_volume; snd_rawmidi_t *rmidi; spinlock_t reg_lock; struct tasklet_struct hwvol_tq; /* Maestro Stuff */ u16 maestro_map[32]; int bobclient; /* active timer instancs */ int bob_freq; /* timer frequency */ struct semaphore 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; struct resource *res_joystick;#endif};static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs);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(es1968_t *chip, u16 reg, u16 data){ outw(reg, chip->io_port + ESM_INDEX); outw(data, chip->io_port + ESM_DATA); chip->maestro_map[reg] = data;}inline static void maestro_write(es1968_t *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(es1968_t *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];}inline static u16 maestro_read(es1968_t *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;}#define big_mdelay(msec) do {\ set_current_state(TASK_UNINTERRUPTIBLE);\ schedule_timeout(((msec) * HZ + 999) / 1000);\} while (0) /* Wait for the codec bus to be free */static int snd_es1968_ac97_wait(es1968_t *chip){ int timeout = 100000; while (timeout-- > 0) { if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1)) return 0; } snd_printd("es1968: ac97 timeout\n"); return 1; /* timeout */}static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val){ es1968_t *chip = ac97->private_data; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); snd_es1968_ac97_wait(chip); /* Write the bus */ outw(val, chip->io_port + ESM_AC97_DATA); mdelay(1); outb(reg, chip->io_port + ESM_AC97_INDEX); mdelay(1); spin_unlock_irqrestore(&chip->reg_lock, flags);}static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg){ u16 data = 0; es1968_t *chip = ac97->private_data; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); snd_es1968_ac97_wait(chip); outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); mdelay(1); if (! snd_es1968_ac97_wait(chip)) { data = inw(chip->io_port + ESM_AC97_DATA); mdelay(1); } spin_unlock_irqrestore(&chip->reg_lock, flags); return data;}/* no spinlock */static void apu_index_set(es1968_t *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(es1968_t *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(es1968_t *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);}inline static void apu_set_register(es1968_t *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(es1968_t *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);}inline static u16 apu_get_register(es1968_t *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(es1968_t *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(es1968_t *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(es1968_t *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(es1968_t *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(es1968_t *chip){ u16 reg; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); 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); spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_es1968_bob_start(es1968_t *chip){ int prescale; int divide; unsigned long flags; /* 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--; spin_lock_irqsave(&chip->reg_lock, flags); __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); spin_unlock_irqrestore(&chip->reg_lock, flags);}/* call with substream spinlock */static void snd_es1968_bob_inc(es1968_t *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(es1968_t *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 */ struct list_head *p; int max_freq = ESM_BOB_FREQ; list_for_each(p, &chip->substream_list) { esschan_t *es = list_entry(p, esschan_t, 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(es1968_t *chip, esschan_t *es, snd_pcm_runtime_t *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)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?