📄 ice1712.c
字号:
/* * ALSA driver for ICEnsemble ICE1712 (Envy24) * * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* NOTES: - spdif nonaudio consumer mode does not work (at least with my Sony STR-DB830)*//* * Changes: * * 2002.09.09 Takashi Iwai <tiwai@suse.de> * split the code to several files. each low-level routine * is stored in the local file and called from registration * function from card_info struct. * * 2002.11.26 James Stafford <jstafford@ampltd.com> * Added support for VT1724 (Envy24HT) * I have left out support for 176.4 and 192 KHz for the moment. * I also haven't done anything with the internal S/PDIF transmitter or the MPU-401 * * 2003.02.20 Taksahi Iwai <tiwai@suse.de> * Split vt1724 part to an independent driver. * The GPIO is accessed through the callback functions now. * * 2004.03.31 Doug McLain <nostar@comcast.net> * Added support for Event Electronics EZ8 card to hoontech.c. */#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/cs8427.h>#include <sound/info.h>#include <sound/mpu401.h>#include <sound/initval.h>#include <sound/asoundef.h>#include "ice1712.h"/* lowlevel routines */#include "delta.h"#include "ews.h"#include "hoontech.h"MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{" HOONTECH_DEVICE_DESC DELTA_DEVICE_DESC EWS_DEVICE_DESC "{ICEnsemble,Generic ICE1712}," "{ICEnsemble,Generic Envy24}}");static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */static char *model[SNDRV_CARDS];static int omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transciever reset timeout value in msec */module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for ICE1712 soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for ICE1712 soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable ICE1712 soundcard.");module_param_array(omni, bool, NULL, 0444);MODULE_PARM_DESC(omni, "Enable Midiman M-Audio Delta Omni I/O support.");module_param_array(cs8427_timeout, int, NULL, 0444);MODULE_PARM_DESC(cs8427_timeout, "Define reset timeout for cs8427 chip in msec resolution.");module_param_array(model, charp, NULL, 0444);MODULE_PARM_DESC(model, "Use the given board model.");static struct pci_device_id snd_ice1712_ids[] = { { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */ { 0, }};MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);static int snd_ice1712_build_pro_mixer(ice1712_t *ice);static int snd_ice1712_build_controls(ice1712_t *ice);static int PRO_RATE_LOCKED;static int PRO_RATE_RESET = 1;static unsigned int PRO_RATE_DEFAULT = 44100;/* * Basic I/O */ /* check whether the clock mode is spdif-in */static inline int is_spdif_master(ice1712_t *ice){ return (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) ? 1 : 0;}static inline int is_pro_rate_locked(ice1712_t *ice){ return is_spdif_master(ice) || PRO_RATE_LOCKED;}static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data){ outb((channel << 4) | addr, ICEDS(ice, INDEX)); outl(data, ICEDS(ice, DATA));}static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr){ outb((channel << 4) | addr, ICEDS(ice, INDEX)); return inl(ICEDS(ice, DATA));}static void snd_ice1712_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val){ ice1712_t *ice = (ice1712_t *)ac97->private_data; int tm; unsigned char old_cmd = 0; for (tm = 0; tm < 0x10000; tm++) { old_cmd = inb(ICEREG(ice, AC97_CMD)); if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) continue; if (!(old_cmd & ICE1712_AC97_READY)) continue; break; } outb(reg, ICEREG(ice, AC97_INDEX)); outw(val, ICEREG(ice, AC97_DATA)); old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD)); for (tm = 0; tm < 0x10000; tm++) if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) break;}static unsigned short snd_ice1712_ac97_read(ac97_t *ac97, unsigned short reg){ ice1712_t *ice = (ice1712_t *)ac97->private_data; int tm; unsigned char old_cmd = 0; for (tm = 0; tm < 0x10000; tm++) { old_cmd = inb(ICEREG(ice, AC97_CMD)); if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) continue; if (!(old_cmd & ICE1712_AC97_READY)) continue; break; } outb(reg, ICEREG(ice, AC97_INDEX)); outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD)); for (tm = 0; tm < 0x10000; tm++) if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) break; if (tm >= 0x10000) /* timeout */ return ~0; return inw(ICEREG(ice, AC97_DATA));}/* * pro ac97 section */static void snd_ice1712_pro_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val){ ice1712_t *ice = (ice1712_t *)ac97->private_data; int tm; unsigned char old_cmd = 0; for (tm = 0; tm < 0x10000; tm++) { old_cmd = inb(ICEMT(ice, AC97_CMD)); if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) continue; if (!(old_cmd & ICE1712_AC97_READY)) continue; break; } outb(reg, ICEMT(ice, AC97_INDEX)); outw(val, ICEMT(ice, AC97_DATA)); old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD)); for (tm = 0; tm < 0x10000; tm++) if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) break;}static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97, unsigned short reg){ ice1712_t *ice = (ice1712_t *)ac97->private_data; int tm; unsigned char old_cmd = 0; for (tm = 0; tm < 0x10000; tm++) { old_cmd = inb(ICEMT(ice, AC97_CMD)); if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) continue; if (!(old_cmd & ICE1712_AC97_READY)) continue; break; } outb(reg, ICEMT(ice, AC97_INDEX)); outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD)); for (tm = 0; tm < 0x10000; tm++) if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) break; if (tm >= 0x10000) /* timeout */ return ~0; return inw(ICEMT(ice, AC97_DATA));}/* * consumer ac97 digital mix */static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0; return 0;}static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned char val, nval; spin_lock_irq(&ice->reg_lock); val = inb(ICEMT(ice, MONITOR_ROUTECTRL)); nval = val & ~ICE1712_ROUTE_AC97; if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97; outb(nval, ICEMT(ice, MONITOR_ROUTECTRL)); spin_unlock_irq(&ice->reg_lock); return val != nval;}static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Mixer To AC97", .info = snd_ice1712_digmix_route_ac97_info, .get = snd_ice1712_digmix_route_ac97_get, .put = snd_ice1712_digmix_route_ac97_put,};/* * gpio operations */static void snd_ice1712_set_gpio_dir(ice1712_t *ice, unsigned int data){ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, data); inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */}static void snd_ice1712_set_gpio_mask(ice1712_t *ice, unsigned int data){ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data); inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */}static unsigned int snd_ice1712_get_gpio_data(ice1712_t *ice){ return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);}static void snd_ice1712_set_gpio_data(ice1712_t *ice, unsigned int val){ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, val); inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */}/* * * CS8427 interface * *//* * change the input clock selection * spdif_clock = 1 - IEC958 input, 0 - Envy24 */static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock){ unsigned char reg[2] = { 0x80 | 4, 0 }; /* CS8427 auto increment | register number 4 + data */ unsigned char val, nval; int res = 0; snd_i2c_lock(ice->i2c); if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } nval = val & 0xf0; if (spdif_clock) nval |= 0x01; else nval |= 0x04; if (val != nval) { reg[1] = nval; if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) { res = -EIO; } else { res++; } } snd_i2c_unlock(ice->i2c); return res;}/* * spdif callbacks */static void open_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream){ snd_cs8427_iec958_active(ice->cs8427, 1);}static void close_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream){ snd_cs8427_iec958_active(ice->cs8427, 0);}static void setup_cs8427(ice1712_t *ice, int rate){ snd_cs8427_iec958_pcm(ice->cs8427, rate);}/* * create and initialize callbacks for cs8427 interface */int __devinit snd_ice1712_init_cs8427(ice1712_t *ice, int addr){ int err; if ((err = snd_cs8427_create(ice->i2c, addr, (ice->cs8427_timeout * HZ) / 1000, &ice->cs8427)) < 0) { snd_printk(KERN_ERR "CS8427 initialization failed\n"); return err; } ice->spdif.ops.open = open_cs8427; ice->spdif.ops.close = close_cs8427; ice->spdif.ops.setup_rate = setup_cs8427; return 0;}/* * Interrupt handler */static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs){ ice1712_t *ice = dev_id; unsigned char status; int handled = 0; while (1) { status = inb(ICEREG(ice, IRQSTAT)); if (status == 0) break; handled = 1; if (status & ICE1712_IRQ_MPU1) { if (ice->rmidi[0]) snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs); outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT)); status &= ~ICE1712_IRQ_MPU1; } if (status & ICE1712_IRQ_TIMER) outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT)); if (status & ICE1712_IRQ_MPU2) { if (ice->rmidi[1]) snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs); outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT)); status &= ~ICE1712_IRQ_MPU2; } if (status & ICE1712_IRQ_PROPCM) { unsigned char mtstat = inb(ICEMT(ice, IRQ)); if (mtstat & ICE1712_MULTI_PBKSTATUS) { if (ice->playback_pro_substream) snd_pcm_period_elapsed(ice->playback_pro_substream); outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ)); } if (mtstat & ICE1712_MULTI_CAPSTATUS) { if (ice->capture_pro_substream) snd_pcm_period_elapsed(ice->capture_pro_substream); outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -