📄 ac97_codec.c
字号:
/* build ADC/DAC loopback control */ if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0) return err; } snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, ~AC97_GP_DRSS_MASK, 0x0000); /* build 3D controls */ if (ac97->build_ops->build_3d) { ac97->build_ops->build_3d(ac97); } else { if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) { unsigned short val; val = 0x0707; snd_ac97_write(ac97, AC97_3D_CONTROL, val); val = snd_ac97_read(ac97, AC97_3D_CONTROL); val = val == 0x0606; if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; if (val) kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0) return err; if (val) kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); } } /* build S/PDIF controls */ /* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */ if (ac97->subsystem_vendor == 0x1043 && ac97->subsystem_device == 0x810f) ac97->ext_id |= AC97_EI_SPDIF; if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) { if (ac97->build_ops->build_spdif) { if ((err = ac97->build_ops->build_spdif(ac97)) < 0) return err; } else { for (idx = 0; idx < 5; idx++) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) return err; if (ac97->build_ops->build_post_spdif) { if ((err = ac97->build_ops->build_post_spdif(ac97)) < 0) return err; } /* set default PCM S/PDIF params */ /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20); ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97); } ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF; } /* build chip specific controls */ if (ac97->build_ops->build_specific) if ((err = ac97->build_ops->build_specific(ac97)) < 0) return err; if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) { kctl = snd_ac97_cnew(&snd_ac97_control_eapd, ac97); if (! kctl) return -ENOMEM; if (ac97->scaps & AC97_SCAP_INV_EAPD) set_inv_eapd(ac97, kctl); if ((err = snd_ctl_add(card, kctl)) < 0) return err; } return 0;}static int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97){ int err, idx; //printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG)); snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH)); snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH)); snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff); snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0); snd_ac97_write(ac97, AC97_MISC_AFE, 0x0); /* build modem switches */ for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++) if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97))) < 0) return err; /* build chip specific controls */ if (ac97->build_ops->build_specific) if ((err = ac97->build_ops->build_specific(ac97)) < 0) return err; return 0;}static int snd_ac97_test_rate(struct snd_ac97 *ac97, int reg, int shadow_reg, int rate){ unsigned short val; 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(struct snd_ac97 *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(struct snd_ac97 *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 struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id *table, unsigned int id){ const struct ac97_codec_id *pid; for (pid = table; pid->id; pid++) if (pid->id == (id & pid->mask)) return pid; return NULL;}void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem){ const struct ac97_codec_id *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(struct snd_ac97 *ac97){ const struct ac97_codec_id *pid; for (pid = snd_ac97_codec_ids; pid->id; pid++) if (pid->id == (ac97->id & pid->mask)) return pid->name; return "unknown codec";}EXPORT_SYMBOL(snd_ac97_get_short_name);/* wait for a while until registers are accessible after RESET * return 0 if ok, negative not ready */static int ac97_reset_wait(struct snd_ac97 *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 struct snd_ac97_bus 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(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops, void *private_data, struct snd_ac97_bus **rbus){ int err; struct snd_ac97_bus *bus; static struct snd_device_ops 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;}EXPORT_SYMBOL(snd_ac97_bus);/* stop no dev release warning */static void ac97_device_release(struct device * dev){}/* register ac97 codec to bus */static int snd_ac97_dev_register(struct snd_device *device){ struct snd_ac97 *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, "%d-%d:%s", ac97->bus->card->number, ac97->num, snd_ac97_get_short_name(ac97)); 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;}/* disconnect ac97 codec */static int snd_ac97_dev_disconnect(struct snd_device *device){ struct snd_ac97 *ac97 = device->device_data; if (ac97->dev.bus) device_unregister(&ac97->dev); return 0;}/* build_ops to do nothing */static struct snd_ac97_build_ops null_build_ops;#ifdef CONFIG_SND_AC97_POWER_SAVEstatic void do_update_power(struct work_struct *work){ update_power_regs( container_of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -