📄 ac97_codec.c
字号:
unsigned int tmp; tmp = ((unsigned int)rate * ac97->bus->clock) / 48000; snd_ac97_write_cache(ac97, reg, tmp & 0xffff); if (shadow_reg) snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff); val = snd_ac97_read(ac97, reg); return val == (tmp & 0xffff);}static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result){ unsigned int result = 0; unsigned short saved; if (ac97->bus->no_vra) { *r_result = SNDRV_PCM_RATE_48000; if ((ac97->flags & AC97_DOUBLE_RATE) && reg == AC97_PCM_FRONT_DAC_RATE) *r_result |= SNDRV_PCM_RATE_96000; return; } saved = snd_ac97_read(ac97, reg); if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE) snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_DRA, 0); /* test a non-standard rate */ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000)) result |= SNDRV_PCM_RATE_CONTINUOUS; /* let's try to obtain standard rates */ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000)) result |= SNDRV_PCM_RATE_8000; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025)) result |= SNDRV_PCM_RATE_11025; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000)) result |= SNDRV_PCM_RATE_16000; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050)) result |= SNDRV_PCM_RATE_22050; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000)) result |= SNDRV_PCM_RATE_32000; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100)) result |= SNDRV_PCM_RATE_44100; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000)) result |= SNDRV_PCM_RATE_48000; if ((ac97->flags & AC97_DOUBLE_RATE) && reg == AC97_PCM_FRONT_DAC_RATE) { /* test standard double rates */ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_DRA, AC97_EA_DRA); if (snd_ac97_test_rate(ac97, reg, shadow_reg, 64000 / 2)) result |= SNDRV_PCM_RATE_64000; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 88200 / 2)) result |= SNDRV_PCM_RATE_88200; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 96000 / 2)) result |= SNDRV_PCM_RATE_96000; /* some codecs don't support variable double rates */ if (!snd_ac97_test_rate(ac97, reg, shadow_reg, 76100 / 2)) result &= ~SNDRV_PCM_RATE_CONTINUOUS; snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_DRA, 0); } /* restore the default value */ snd_ac97_write_cache(ac97, reg, saved); if (shadow_reg) snd_ac97_write_cache(ac97, shadow_reg, saved); *r_result = result;}/* check AC97_SPDIF register to accept which sample rates */static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97){ unsigned int result = 0; int i; static unsigned short ctl_bits[] = { AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K }; static unsigned int rate_bits[] = { SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000 }; for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) { snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]); if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i]) result |= rate_bits[i]; } return result;}/* look for the codec id table matching with the given id */static const ac97_codec_id_t *look_for_codec_id(const ac97_codec_id_t *table, unsigned int id){ const ac97_codec_id_t *pid; for (pid = table; pid->id; pid++) if (pid->id == (id & pid->mask)) return pid; return NULL;}void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem){ const ac97_codec_id_t *pid; sprintf(name, "0x%x %c%c%c", id, printable(id >> 24), printable(id >> 16), printable(id >> 8)); pid = look_for_codec_id(snd_ac97_codec_id_vendors, id); if (! pid) return; strcpy(name, pid->name); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || (! modem && ! (pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) { strcat(name, " "); strcat(name, pid->name); if (pid->mask != 0xffffffff) sprintf(name + strlen(name), " rev %d", id & ~pid->mask); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || (! modem && ! (pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } } else sprintf(name + strlen(name), " id %x", id & 0xff);}/** * snd_ac97_get_short_name - retrieve codec name * @ac97: the codec instance * * Returns the short identifying name of the codec. */const char *snd_ac97_get_short_name(ac97_t *ac97){ const ac97_codec_id_t *pid; for (pid = snd_ac97_codec_ids; pid->id; pid++) if (pid->id == (ac97->id & pid->mask)) return pid->name; return "unknown codec";}/* wait for a while until registers are accessible after RESET * return 0 if ok, negative not ready */static int ac97_reset_wait(ac97_t *ac97, int timeout, int with_modem){ unsigned long end_time; unsigned short val; end_time = jiffies + timeout; do { /* use preliminary reads to settle the communication */ snd_ac97_read(ac97, AC97_RESET); snd_ac97_read(ac97, AC97_VENDOR_ID1); snd_ac97_read(ac97, AC97_VENDOR_ID2); /* modem? */ if (with_modem) { val = snd_ac97_read(ac97, AC97_EXTENDED_MID); if (val != 0xffff && (val & 1) != 0) return 0; } if (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) { /* probably only Xbox issue - all registers are read as zero */ val = snd_ac97_read(ac97, AC97_VENDOR_ID1); if (val != 0 && val != 0xffff) return 0; } else { /* because the PCM or MASTER volume registers can be modified, * the REC_GAIN register is used for tests */ /* test if we can write to the record gain volume register */ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); if ((snd_ac97_read(ac97, AC97_REC_GAIN) & 0x7fff) == 0x0a05) return 0; } schedule_timeout_uninterruptible(1); } while (time_after_eq(end_time, jiffies)); return -ENODEV;}/** * snd_ac97_bus - create an AC97 bus component * @card: the card instance * @num: the bus number * @ops: the bus callbacks table * @private_data: private data pointer for the new instance * @rbus: the pointer to store the new AC97 bus instance. * * Creates an AC97 bus component. An ac97_bus_t instance is newly * allocated and initialized. * * The ops table must include valid callbacks (at least read and * write). The other callbacks, wait and reset, are not mandatory. * * The clock is set to 48000. If another clock is needed, set * (*rbus)->clock manually. * * The AC97 bus instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */int snd_ac97_bus(snd_card_t *card, int num, ac97_bus_ops_t *ops, void *private_data, ac97_bus_t **rbus){ int err; ac97_bus_t *bus; static snd_device_ops_t dev_ops = { .dev_free = snd_ac97_bus_dev_free, }; snd_assert(card != NULL, return -EINVAL); snd_assert(rbus != NULL, return -EINVAL); bus = kzalloc(sizeof(*bus), GFP_KERNEL); if (bus == NULL) return -ENOMEM; bus->card = card; bus->num = num; bus->ops = ops; bus->private_data = private_data; bus->clock = 48000; spin_lock_init(&bus->bus_lock); snd_ac97_bus_proc_init(bus); if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) { snd_ac97_bus_free(bus); return err; } *rbus = bus; return 0;}/* stop no dev release warning */static void ac97_device_release(struct device * dev){}/* register ac97 codec to bus */static int snd_ac97_dev_register(snd_device_t *device){ ac97_t *ac97 = device->device_data; int err; ac97->dev.bus = &ac97_bus_type; ac97->dev.parent = ac97->bus->card->dev; ac97->dev.release = ac97_device_release; snprintf(ac97->dev.bus_id, BUS_ID_SIZE, "card%d-%d", ac97->bus->card->number, ac97->num); if ((err = device_register(&ac97->dev)) < 0) { snd_printk(KERN_ERR "Can't register ac97 bus\n"); ac97->dev.bus = NULL; return err; } return 0;}/* unregister ac97 codec */static int snd_ac97_dev_unregister(snd_device_t *device){ ac97_t *ac97 = device->device_data; if (ac97->dev.bus) device_unregister(&ac97->dev); return snd_ac97_free(ac97);}/* build_ops to do nothing */static struct snd_ac97_build_ops null_build_ops;/** * snd_ac97_mixer - create an Codec97 component * @bus: the AC97 bus which codec is attached to * @template: the template of ac97, including index, callbacks and * the private data. * @rac97: the pointer to store the new ac97 instance. * * Creates an Codec97 component. An ac97_t instance is newly * allocated and initialized from the template. The codec * is then initialized by the standard procedure. * * The template must include the codec number (num) and address (addr), * and the private data (private_data). * * The ac97 instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97){ int err; ac97_t *ac97; snd_card_t *card; char name[64]; unsigned long end_time; unsigned int reg; const ac97_codec_id_t *pid; static snd_device_ops_t ops = { .dev_free = snd_ac97_dev_free, .dev_register = snd_ac97_dev_register, .dev_unregister = snd_ac97_dev_unregister, }; snd_assert(rac97 != NULL, return -EINVAL); *rac97 = NULL; snd_assert(bus != NULL && template != NULL, return -EINVAL); snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL); card = bus->card; ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL); if (ac97 == NULL) return -ENOMEM; ac97->private_data = template->private_data; ac97->private_free = template->private_free; ac97->bus = bus; ac97->pci = template->pci; ac97->num = template->num; ac97->addr = template->addr; ac97->scaps = template->scaps; ac97->limited_regs = template->limited_regs; memcpy(ac97->reg_accessed, template->reg_accessed, sizeof(ac97->reg_accessed)); bus->codec[ac97->num] = ac97; init_MUTEX(&ac97->reg_mutex); init_MUTEX(&ac97->page_mutex); if (ac97->pci) { pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor); pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device); } if (bus->ops->reset) { bus->ops->reset(ac97); goto __access_ok; } ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); if (ac97->id && ac97->id != (unsigned int)-1) { pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id); if (pid && (pid->flags & AC97_DEFAULT_POWER_OFF)) goto __access_ok; } /* reset to defaults */ if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO)) snd_ac97_write(ac97, AC97_RESET, 0); if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM)) snd_ac97_write(ac97, AC97_EXTENDED_MID, 0); if (bus->ops->wait) bus->ops->wait(ac97); else { udelay(50); if (ac97->scaps & AC97_SCAP_SKIP_AUDIO) err = ac97_reset_wait(ac97, HZ/2, 1); else { err = ac97_reset_wait(ac97, HZ/2, 0); if (err < 0) err = ac97_reset_wait(ac97, HZ/2, 1); } if (err < 0) { snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num); /* proceed anyway - it's often non-critical */ } } __access_ok: ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) && (ac97->id == 0x00000000 || ac97->id == 0xffffffff)) { snd_printk(KERN_ERR "AC'97 %d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->id); snd_ac97_free(ac97); return -EIO; } pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id); if (pid) ac97->flags |= pid->flags; /* test for AC'97 */ if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) { /* test if we can write to the record gain volume register */ snd_ac97_write_ca
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -