📄 cs46xx_lib.c
字号:
chip->midcr |= MIDCR_RIE; snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } else { if (chip->midcr & MIDCR_RIE) { chip->midcr &= ~MIDCR_RIE; snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_cs46xx_midi_output_trigger(snd_rawmidi_substream_t * substream, int up){ unsigned long flags; cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); unsigned char byte; spin_lock_irqsave(&chip->reg_lock, flags); if (up) { if ((chip->midcr & MIDCR_TIE) == 0) { chip->midcr |= MIDCR_TIE; /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ while ((chip->midcr & MIDCR_TIE) && (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { chip->midcr &= ~MIDCR_TIE; } else { snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte); } } snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } else { if (chip->midcr & MIDCR_TIE) { chip->midcr &= ~MIDCR_TIE; snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } spin_unlock_irqrestore(&chip->reg_lock, flags);}static snd_rawmidi_ops_t snd_cs46xx_midi_output ={ open: snd_cs46xx_midi_output_open, close: snd_cs46xx_midi_output_close, trigger: snd_cs46xx_midi_output_trigger,};static snd_rawmidi_ops_t snd_cs46xx_midi_input ={ open: snd_cs46xx_midi_input_open, close: snd_cs46xx_midi_input_close, trigger: snd_cs46xx_midi_input_trigger,};int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi){ snd_rawmidi_t *rmidi; int err; if (rrawmidi) *rrawmidi = NULL; if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0) return err; strcpy(rmidi->name, "CS46XX"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = chip; chip->rmidi = rmidi; if (rrawmidi) *rrawmidi = NULL; return 0;}/* * gameport interface */#ifndef LINUX_2_2typedef struct snd_cs46xx_gameport { struct gameport info; cs46xx_t *chip;} cs46xx_gameport_t;static void snd_cs46xx_gameport_trigger(struct gameport *gameport){ cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; cs46xx_t *chip; snd_assert(gp, return); chip = snd_magic_cast(cs46xx_t, gp->chip, return); snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF);}static unsigned char snd_cs46xx_gameport_read(struct gameport *gameport){ cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; cs46xx_t *chip; snd_assert(gp, return 0); chip = snd_magic_cast(cs46xx_t, gp->chip, return 0); return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io);}static int snd_cs46xx_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons){ cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; cs46xx_t *chip; unsigned js1, js2, jst; snd_assert(gp, return 0); chip = snd_magic_cast(cs46xx_t, gp->chip, return 0); js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1); js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2); jst = snd_cs46xx_peekBA0(chip, BA0_JSPT); *buttons = (~jst >> 4) & 0x0F; axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; for(jst=0;jst<4;++jst) if(axes[jst]==0xFFFF) axes[jst] = -1; return 0;}static int snd_cs46xx_gameport_open(struct gameport *gameport, int mode){ switch (mode) { case GAMEPORT_MODE_COOKED: return 0; case GAMEPORT_MODE_RAW: return 0; default: return -1; } return 0;}void __devinit snd_cs46xx_gameport(cs46xx_t *chip){ cs46xx_gameport_t *gp; gp = kmalloc(sizeof(*gp), GFP_KERNEL); if (! gp) { snd_printk("cannot allocate gameport area\n"); return; } memset(gp, 0, sizeof(*gp)); gp->info.open = snd_cs46xx_gameport_open; gp->info.read = snd_cs46xx_gameport_read; gp->info.trigger = snd_cs46xx_gameport_trigger; gp->info.cooked_read = snd_cs46xx_gameport_cooked_read; gp->chip = chip; chip->gameport = gp; snd_cs46xx_pokeBA0(chip, BA0_JSIO, 0xFF); // ? snd_cs46xx_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); gameport_register_port(&gp->info);}#else /* LINUX_2_2 */void __devinit snd_cs46xx_gameport(cs46xx_t *chip){}#endif /* !LINUX_2_2 *//* * proc interface */static long snd_cs46xx_io_read(snd_info_entry_t *entry, void *file_private_data, struct file *file, char *buf, long count){ long size; snd_cs46xx_region_t *region = (snd_cs46xx_region_t *)entry->private_data; size = count; if (file->f_pos + size > region->size) size = region->size - file->f_pos; if (size > 0) { char *tmp; long res; unsigned long virt; if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) return -ENOMEM; virt = region->remap_addr + file->f_pos; memcpy_fromio(tmp, virt, size); if (copy_to_user(buf, tmp, size)) res = -EFAULT; else { res = size; file->f_pos += size; } kfree(tmp); return res; } return 0;}static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = { read: snd_cs46xx_io_read,};static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip){ snd_info_entry_t *entry; int idx; for (idx = 0; idx < 5; idx++) { snd_cs46xx_region_t *region = &chip->region.idx[idx]; entry = snd_info_create_card_entry(card, region->name, card->proc_root); if (entry) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; entry->c.ops = &snd_cs46xx_proc_io_ops; entry->size = region->size; entry->mode = S_IFREG | S_IRUSR; if (snd_info_register(entry) < 0) { snd_info_unregister(entry); entry = NULL; } } region->proc_entry = entry; } return 0;}static int snd_cs46xx_proc_done(cs46xx_t *chip){ int idx; for (idx = 0; idx < 5; idx++) { snd_cs46xx_region_t *region = &chip->region.idx[idx]; if (region->proc_entry) { snd_info_unregister((snd_info_entry_t *) region->proc_entry); region->proc_entry = NULL; } } return 0;}/* * stop the h/w */static void snd_cs46xx_hw_stop(cs46xx_t *chip){ unsigned int tmp; tmp = snd_cs46xx_peek(chip, BA1_PFIE); tmp &= ~0x0000f03f; tmp |= 0x00000010; snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt disable */ tmp = snd_cs46xx_peek(chip, BA1_CIE); tmp &= ~0x0000003f; tmp |= 0x00000011; snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt disable */ /* * Stop playback DMA. */ tmp = snd_cs46xx_peek(chip, BA1_PCTL); snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); /* * Stop capture DMA. */ tmp = snd_cs46xx_peek(chip, BA1_CCTL); snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); /* * Reset the processor. */ snd_cs46xx_reset(chip); snd_cs46xx_proc_stop(chip); /* * Power down the PLL. */ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); /* * Turn off the Processor by turning off the software clock enable flag in * the clock control register. */ tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);}static int snd_cs46xx_free(cs46xx_t *chip){ int idx; snd_assert(chip != NULL, return -EINVAL); if (chip->active_ctrl) chip->active_ctrl(chip, 1);#ifndef LINUX_2_2 if (chip->gameport) { gameport_unregister_port(&chip->gameport->info); kfree(chip->gameport); }#endif#ifdef CONFIG_PM if (chip->pm_dev) pm_unregister(chip->pm_dev);#endif if (chip->amplifier_ctrl) chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */ snd_cs46xx_proc_done(chip); if (chip->region.idx[0].resource) snd_cs46xx_hw_stop(chip); for (idx = 0; idx < 5; idx++) { snd_cs46xx_region_t *region = &chip->region.idx[idx]; if (region->remap_addr) iounmap((void *) region->remap_addr); if (region->resource) { release_resource(region->resource); kfree_nocheck(region->resource); } } if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); if (chip->active_ctrl) chip->active_ctrl(chip, -chip->amplifier); snd_magic_kfree(chip); return 0;}static int snd_cs46xx_dev_free(snd_device_t *device){ cs46xx_t *chip = snd_magic_cast(cs46xx_t, device->device_data, return -ENXIO); return snd_cs46xx_free(chip);}/* * initialize chip */static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait){ unsigned int tmp; int timeout; /* * First, blast the clock control register to zero so that the PLL starts * out in a known state, and blast the master serial port control register * to zero so that the serial ports also start out in a known state. */ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0); /* * If we are in AC97 mode, then we must set the part to a host controlled * AC-link. Otherwise, we won't be able to bring up the link. */ snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03); /* 1.03 codec */ /* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */ /* * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 * spec) and then drive it high. This is done for non AC97 modes since * there might be logic external to the CS461x that uses the ARST# line * for a reset. */ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0); udelay(50); snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN); /* * The first thing we do here is to enable sync generation. As soon * as we start receiving bit clock, we'll start producing the SYNC * signal. */ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); /* * Now wait for a short while to allow the AC97 part to start * generating bit clock (so we don't try to start the PLL without an * input clock). */ mdelay(1); /* * Set the serial port timing configuration, so that * the clock control circuit gets its clock from the correct place. */ snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97); /* * Write the selected clock control setup to the hardware. Do not turn on * SWCE yet (if requested), so that the devices clocked by the output of * PLL are not clocked until the PLL is stable. */ snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a); snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8); /* * Power up the PLL. */ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP); /* * Wait until the PLL has stabilized. */ mdelay(1); /* * Turn on clocking of the core so that we can setup the serial ports. */ snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE); /* * Fill the serial port FIFOs with silence. */ snd_cs46xx_clear_serial_FIFOs(chip); /* * Set the serial port FIFO pointer to the first sample in the FIFO. */ /* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */ /* * Write the serial port configuration to the part. The master * enable bit is not set until all other values have been written. */ snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); /* * Wait for the codec ready signal from the AC97 codec. */ timeout = 150; while (timeout-- > 0) { /* * Read the AC97 status register to see if we've seen a CODEC READY * signal from the AC97 codec. */ if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY) goto ok1; if (busywait) mdelay(10); else { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((HZ+99)/100); } } snd_printk("create - never read codec ready from AC'97\n"); snd_printk("it is not probably bug, try to use CS4236 driver\n"); snd_cs46xx_free(chip); return -EIO; ok1: /* * Assert the vaid frame signal so that we can start sending commands * to the AC97 codec.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -