// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> * Universal interface for Audio Codec '97 * * For more details look to AC '97 component specification revision 2.2 * by Intel Corporation (http://developer.intel.com).
*/
staticint snd_ac97_valid_reg(struct snd_ac97 *ac97, unsignedshort reg)
{ /* filter some registers for buggy codecs */ switch (ac97->id) { case AC97_ID_ST_AC97_ID4: if (reg == 0x08) return 0;
fallthrough; case AC97_ID_ST7597: if (reg == 0x22 || reg == 0x7a) return 1;
fallthrough; case AC97_ID_AK4540: case AC97_ID_AK4542: if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c) return 1; return 0; case AC97_ID_AD1819: /* AD1819 */ case AC97_ID_AD1881: /* AD1881 */ case AC97_ID_AD1881A: /* AD1881A */ if (reg >= 0x3a && reg <= 0x6e) /* 0x59 */ return 0; return 1; case AC97_ID_AD1885: /* AD1885 */ case AC97_ID_AD1886: /* AD1886 */ case AC97_ID_AD1886A: /* AD1886A - !!verify!! --jk */ case AC97_ID_AD1887: /* AD1887 - !!verify!! --jk */ if (reg == 0x5a) return 1; if (reg >= 0x3c && reg <= 0x6e) /* 0x59 */ return 0; return 1; case AC97_ID_STAC9700: case AC97_ID_STAC9704: case AC97_ID_STAC9705: case AC97_ID_STAC9708: case AC97_ID_STAC9721: case AC97_ID_STAC9744: case AC97_ID_STAC9756: if (reg <= 0x3a || reg >= 0x5a) return 1; return 0;
} return 1;
}
/** * snd_ac97_write - write a value on the given register * @ac97: the ac97 instance * @reg: the register to change * @value: the value to set * * Writes a value on the given register. This will invoke the write * callback directly after the register check. * This function doesn't change the register cache unlike * #snd_ca97_write_cache(), so use this only when you don't want to * reflect the change to the suspend/resume state.
*/ void snd_ac97_write(struct snd_ac97 *ac97, unsignedshort reg, unsignedshort value)
{ if (!snd_ac97_valid_reg(ac97, reg)) return; if ((ac97->id & 0xffffff00) == AC97_ID_ALC100) { /* Fix H/W bug of ALC100/100P */ if (reg == AC97_MASTER || reg == AC97_HEADPHONE)
ac97->bus->ops->write(ac97, AC97_RESET, 0); /* reset audio codec */
}
ac97->bus->ops->write(ac97, reg, value);
}
EXPORT_SYMBOL(snd_ac97_write);
/** * snd_ac97_read - read a value from the given register * * @ac97: the ac97 instance * @reg: the register to read * * Reads a value from the given register. This will invoke the read * callback directly after the register check. * * Return: The read value.
*/ unsignedshort snd_ac97_read(struct snd_ac97 *ac97, unsignedshort reg)
{ if (!snd_ac97_valid_reg(ac97, reg)) return 0; return ac97->bus->ops->read(ac97, reg);
}
/* read a register - return the cached value if already read */ staticinlineunsignedshort snd_ac97_read_cache(struct snd_ac97 *ac97, unsignedshort reg)
{ if (! test_bit(reg, ac97->reg_accessed)) {
ac97->regs[reg] = ac97->bus->ops->read(ac97, reg); // set_bit(reg, ac97->reg_accessed);
} return ac97->regs[reg];
}
EXPORT_SYMBOL(snd_ac97_read);
/** * snd_ac97_write_cache - write a value on the given register and update the cache * @ac97: the ac97 instance * @reg: the register to change * @value: the value to set * * Writes a value on the given register and updates the register * cache. The cached values are used for the cached-read and the * suspend/resume.
*/ void snd_ac97_write_cache(struct snd_ac97 *ac97, unsignedshort reg, unsignedshort value)
{ if (!snd_ac97_valid_reg(ac97, reg)) return;
mutex_lock(&ac97->reg_mutex);
ac97->regs[reg] = value;
ac97->bus->ops->write(ac97, reg, value);
set_bit(reg, ac97->reg_accessed);
mutex_unlock(&ac97->reg_mutex);
}
EXPORT_SYMBOL(snd_ac97_write_cache);
/** * snd_ac97_update - update the value on the given register * @ac97: the ac97 instance * @reg: the register to change * @value: the value to set * * Compares the value with the register cache and updates the value * only when the value is changed. * * Return: 1 if the value is changed, 0 if no change, or a negative * code on failure.
*/ int snd_ac97_update(struct snd_ac97 *ac97, unsignedshort reg, unsignedshort value)
{ int change;
/** * snd_ac97_update_bits - update the bits on the given register * @ac97: the ac97 instance * @reg: the register to change * @mask: the bit-mask to change * @value: the value to set * * Updates the masked-bits on the given register only when the value * is changed. * * Return: 1 if the bits are changed, 0 if no change, or a negative * code on failure.
*/ int snd_ac97_update_bits(struct snd_ac97 *ac97, unsignedshort reg, unsignedshort mask, unsignedshort value)
{ int change;
/* no lock version - see snd_ac97_update_bits() */ int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsignedshort reg, unsignedshort mask, unsignedshort value)
{ int change; unsignedshort old, new;
old = snd_ac97_read_cache(ac97, reg); new = (old & ~mask) | (value & mask);
change = old != new; if (change) {
ac97->regs[reg] = new;
ac97->bus->ops->write(ac97, reg, new);
}
set_bit(reg, ac97->reg_accessed); return change;
}
staticint snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, unsignedshort mask, unsignedshort value)
{ int change; unsignedshort old, new, cfg;
new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO); if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) { new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015); switch (new & IEC958_AES0_PRO_FS) { case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break; case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break; case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break; default: val |= 1<<12; break;
} if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
val |= 1<<3;
} else { new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT); new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8); new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24); if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
val |= 1<<3; if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT))
val |= 1<<2;
val |= ((new >> 8) & 0xff) << 4; // category + original switch ((new >> 24) & 0xff) { case IEC958_AES3_CON_FS_44100: val |= 0<<12; break; case IEC958_AES3_CON_FS_48000: val |= 2<<12; break; case IEC958_AES3_CON_FS_32000: val |= 3<<12; break; default: val |= 1<<12; break;
}
}
switch (reg) { case AC97_MASTER_TONE: return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0; case AC97_HEADPHONE: return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0; case AC97_REC_GAIN_MIC: return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0; case AC97_3D_CONTROL: if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) {
val = snd_ac97_read(ac97, reg); /* if nonzero - fixed and we can't set it */ return val == 0;
} return 0; case AC97_CENTER_LFE_MASTER: /* center */ if ((ac97->ext_id & AC97_EI_CDAC) == 0) return 0; break; case AC97_CENTER_LFE_MASTER+1: /* lfe */ if ((ac97->ext_id & AC97_EI_LDAC) == 0) return 0;
reg = AC97_CENTER_LFE_MASTER;
mask = 0x0080; break; case AC97_SURROUND_MASTER: if ((ac97->ext_id & AC97_EI_SDAC) == 0) return 0; break;
}
val = snd_ac97_read(ac97, reg); if (!(val & mask)) { /* nothing seems to be here - mute flag is not set */ /* try another test */
snd_ac97_write_cache(ac97, reg, val | mask);
val = snd_ac97_read(ac97, reg);
val = snd_ac97_read(ac97, reg); if (!(val & mask)) return 0; /* nothing here */
} return 1; /* success, useable */
}
/* first look up the static resolution table */ if (ac97->res_table) { conststruct snd_ac97_res_table *tbl; for (tbl = ac97->res_table; tbl->reg; tbl++) { if (tbl->reg == reg) {
*lo_max = tbl->bits & 0xff;
*hi_max = (tbl->bits >> 8) & 0xff; return;
}
}
}
*lo_max = *hi_max = 0; for (i = 0 ; i < ARRAY_SIZE(cbit); i++) { unsignedshort val;
snd_ac97_write(
ac97, reg,
AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8)
); /* Do the read twice due to buffers on some ac97 codecs. * e.g. The STAC9704 returns exactly what you wrote to the register * if you read it immediately. This causes the detect routine to fail.
*/
val = snd_ac97_read(ac97, reg);
val = snd_ac97_read(ac97, reg); if (! *lo_max && (val & 0x7f) == cbit[i])
*lo_max = max[i]; if (! *hi_max && ((val >> 8) & 0x7f) == cbit[i])
*hi_max = max[i]; if (*lo_max && *hi_max) break;
}
}
staticint snd_ac97_try_bit(struct snd_ac97 * ac97, int reg, int bit)
{ unsignedshort mask, val, orig, res;
mask = 1 << bit;
orig = snd_ac97_read(ac97, reg);
val = orig ^ mask;
snd_ac97_write(ac97, reg, val);
res = snd_ac97_read(ac97, reg);
snd_ac97_write_cache(ac97, reg, orig); return res == val;
}
/* check the volume resolution of center/lfe */ staticvoid snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int shift, unsignedchar *max)
{ unsignedshort val, val1;
*max = 63;
val = AC97_MUTE_MASK_STEREO | (0x20 << shift);
snd_ac97_write(ac97, reg, val);
val1 = snd_ac97_read(ac97, reg); if (val != val1) {
*max = 31;
} /* reset volume to zero */
snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO);
}
staticinlineint printable(unsignedint x)
{
x &= 0xff; if (x < ' ' || x >= 0x71) { if (x <= 0x89) return x - 0x71 + 'A'; return'?';
} return x;
}
/* * create a mute-switch and a volume for normal stereo/mono controls
*/ staticint snd_ac97_cmix_new_stereo(struct snd_card *card, constchar *pfx, int reg, int check_stereo, int check_amix, struct snd_ac97 *ac97)
{ int err; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; unsignedchar lo_max, hi_max;
/* build master controls */ /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */ if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { if (ac97->flags & AC97_HAS_NO_MASTER_VOL)
err = snd_ac97_cmute_new(card, "Master Playback Switch",
AC97_MASTER, 0, ac97); else
err = snd_ac97_cmix_new(card, "Master Playback",
AC97_MASTER, 0, ac97); if (err < 0) return err;
}
/* 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)) { unsignedshort val;
val = 0x0707;
snd_ac97_write(ac97, AC97_3D_CONTROL, val);
val = snd_ac97_read(ac97, AC97_3D_CONTROL);
val = val == 0x0606;
kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
err = snd_ctl_add(card, kctl); if (err < 0) return err; if (val)
kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16);
kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97);
err = snd_ctl_add(card, kctl); if (err < 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;
/* build chip specific controls */ if (ac97->build_ops->build_specific) {
err = ac97->build_ops->build_specific(ac97); if (err < 0) return err;
}
return 0;
}
staticint snd_ac97_test_rate(struct snd_ac97 *ac97, int reg, int shadow_reg, int rate)
{ unsignedshort val; unsignedint tmp;
tmp = ((unsignedint)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);
}
staticvoid snd_ac97_determine_rates(struct snd_ac97 *ac97, int reg, int shadow_reg, unsignedint *r_result)
{ unsignedint result = 0; unsignedshort 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 */ staticunsignedint snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97)
{ unsignedint result = 0; int i; staticconstunsignedshort ctl_bits[] = {
AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K
}; staticconstunsignedint 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 */ staticconststruct ac97_codec_id *look_for_codec_id(conststruct ac97_codec_id *table, unsignedint id)
{ conststruct ac97_codec_id *pid;
for (pid = table; pid->id; pid++) if (pid->id == (id & pid->mask)) return pid; return NULL;
}
pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) {
strlcat(name, " ", maxlen);
strlcat(name, pid->name, maxlen); if (pid->mask != 0xffffffff)
sprintf(name + strlen(name), " rev %u", id & ~pid->mask); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
pid->patch(ac97);
}
} else { int l = strlen(name);
snprintf(name + l, maxlen - l, " id %x", id & 0xff);
}
}
/** * snd_ac97_get_short_name - retrieve codec name * @ac97: the codec instance * * Return: The short identifying name of the codec.
*/ constchar *snd_ac97_get_short_name(struct snd_ac97 *ac97)
{ conststruct 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
*/ staticint ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem)
{ unsignedlong end_time; unsignedshort 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. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_ac97_bus(struct snd_card *card, int num, conststruct snd_ac97_bus_ops *ops, void *private_data, struct snd_ac97_bus **rbus)
{ int err; struct snd_ac97_bus *bus; staticconststruct snd_device_ops dev_ops = {
.dev_free = snd_ac97_bus_dev_free,
};
/** * 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 struct snd_ac97 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. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, structsnd_ac97 **rac97)
{ int err; struct snd_ac97 *ac97; struct snd_card *card; char name[64]; unsignedlong end_time; unsignedint reg; conststruct ac97_codec_id *pid; staticconststruct snd_device_ops ops = {
.dev_free = snd_ac97_dev_free,
.dev_register = snd_ac97_dev_register,
.dev_disconnect = snd_ac97_dev_disconnect,
};
if (snd_BUG_ON(!bus || !template || !rac97)) return -EINVAL;
*rac97 = NULL; if (snd_BUG_ON(template->num >= 4)) return -EINVAL; if (bus->codec[template->num]) return -EBUSY;
/* 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, msecs_to_jiffies(500), 1); else {
err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 0); if (err < 0)
err = ac97_reset_wait(ac97,
msecs_to_jiffies(500), 1);
} if (err < 0) {
ac97_warn(ac97, "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)) {
ac97_err(ac97, "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_cache(ac97, AC97_REC_GAIN, 0x8a06);
err = snd_ac97_read(ac97, AC97_REC_GAIN); if ((err & 0x7fff) == 0x0a06)
ac97->scaps |= AC97_SCAP_AUDIO;
} if (ac97->scaps & AC97_SCAP_AUDIO) {
ac97->caps = snd_ac97_read(ac97, AC97_RESET);
ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); if (ac97->ext_id == 0xffff) /* invalid combination */
ac97->ext_id = 0;
}
/* test for MC'97 */ if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM) && !(ac97->scaps & AC97_SCAP_MODEM)) {
ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); if (ac97->ext_mid == 0xffff) /* invalid combination */
ac97->ext_mid = 0; if (ac97->ext_mid & 1)
ac97->scaps |= AC97_SCAP_MODEM;
}
if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) { if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM)))
ac97_err(ac97, "AC'97 %d access error (not audio or modem codec)\n",
ac97->num);
snd_ac97_free(ac97); return -EACCES;
}
if (bus->ops->reset) // FIXME: always skipping? goto __ready_ok;
/* FIXME: add powerdown control */ if (ac97_is_audio(ac97)) { /* nothing should be in powerdown mode */
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0); if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */
udelay(100);
snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
} /* nothing should be in powerdown mode */
snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
end_time = jiffies + msecs_to_jiffies(5000); do { if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) goto __ready_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
ac97_warn(ac97, "AC'97 %d analog subsections not ready\n", ac97->num);
}
/* FIXME: add powerdown control */ if (ac97_is_modem(ac97)) { unsignedchar tmp;
/* nothing should be in powerdown mode */ /* note: it's important to set the rate at first */
tmp = AC97_MEA_GPIO; if (ac97->ext_mid & AC97_MEI_LINE1) {
snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 8000);
tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1;
} if (ac97->ext_mid & AC97_MEI_LINE2) {
snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 8000);
tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2;
} if (ac97->ext_mid & AC97_MEI_HANDSET) {
snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 8000);
tmp |= AC97_MEA_HADC | AC97_MEA_HDAC;
}
snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
udelay(100); /* nothing should be in powerdown mode */
snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
end_time = jiffies + msecs_to_jiffies(100); do { if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp) goto __ready_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
ac97_warn(ac97, "MC'97 %d converters and GPIO not ready (0x%x)\n",
ac97->num,
snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
}
__ready_ok: if (ac97_is_audio(ac97))
ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT; else
ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT; if (ac97->ext_id & 0x01c9) { /* L/R, MIC, SDAC, LDAC VRA support */
reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
reg |= ac97->ext_id & 0x01c0; /* LDAC/SDAC/CDAC */ if (! bus->no_vra)
reg |= ac97->ext_id & 0x0009; /* VRA/VRM */
snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
} if ((ac97->ext_id & AC97_EI_DRA) && bus->dra) { /* Intel controllers require double rate data to be put in
* slots 7+8, so let's hope the codec supports it. */
snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, AC97_GP_DRSS_78); if ((snd_ac97_read(ac97, AC97_GENERAL_PURPOSE) & AC97_GP_DRSS_MASK) == AC97_GP_DRSS_78)
ac97->flags |= AC97_DOUBLE_RATE; /* restore to slots 10/11 to avoid the confliction with surrounds */
snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, 0);
} if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */
snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]);
snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]);
} else {
ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000; if (ac97->flags & AC97_DOUBLE_RATE)
ac97->rates[AC97_RATES_FRONT_DAC] |= SNDRV_PCM_RATE_96000;
ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
} if (ac97->ext_id & AC97_EI_SPDIF) { /* codec specific code (patch) should override these values */
ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000;
} if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */
snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]);
} else {
ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
} if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */
snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
ac97->scaps |= AC97_SCAP_SURROUND_DAC;
} if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */
snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
} /* additional initializations */ if (bus->ops->init)
bus->ops->init(ac97);
snd_ac97_get_name(ac97, ac97->id, name, sizeof(name), !ac97_is_audio(ac97));
snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code if (! ac97->build_ops)
ac97->build_ops = &null_build_ops;
/* * Power down the chip. * * MASTER and HEADPHONE registers are muted but the register cache values * are not changed, so that the values can be restored in snd_ac97_resume().
*/ staticvoid snd_ac97_powerdown(struct snd_ac97 *ac97)
{ unsignedshort power;
if (ac97_is_audio(ac97)) { /* some codecs have stereo mute bits */
snd_ac97_write(ac97, AC97_MASTER, 0x9f9f);
snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f);
}
/* surround, CLFE, mic powerdown */
power = ac97->regs[AC97_EXTENDED_STATUS]; if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
power |= AC97_EA_PRJ; if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
power |= AC97_EA_PRI | AC97_EA_PRK;
power |= AC97_EA_PRL;
snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power);
/* powerdown external amplifier */ if (ac97->scaps & AC97_SCAP_INV_EAPD)
power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD; elseif (! (ac97->scaps & AC97_SCAP_EAPD_LED))
power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD;
power |= AC97_PD_PR6; /* Headphone amplifier powerdown */
power |= AC97_PD_PR0 | AC97_PD_PR1; /* ADC & DAC powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power);
udelay(100);
power |= AC97_PD_PR2; /* Analog Mixer powerdown (Vref on) */
snd_ac97_write(ac97, AC97_POWERDOWN, power); if (ac97_is_power_save_mode(ac97)) {
power |= AC97_PD_PR3; /* Analog Mixer powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power);
udelay(100); /* AC-link powerdown, internal Clk disable */ /* FIXME: this may cause click noises on some boards */
power |= AC97_PD_PR4 | AC97_PD_PR5;
snd_ac97_write(ac97, AC97_POWERDOWN, power);
}
}
#ifdef CONFIG_SND_AC97_POWER_SAVE /** * snd_ac97_update_power - update the powerdown register * @ac97: the codec instance * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE * @powerup: non-zero when power up the part * * Update the AC97 powerdown register bits of the given part. * * Return: Zero.
*/ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
{ int i;
if (! ac97) return 0;
if (reg) { /* SPDIF requires DAC power, too */ if (reg == AC97_SPDIF)
reg = AC97_PCM_FRONT_DAC_RATE; for (i = 0; i < PWIDX_SIZE; i++) { if (power_regs[i].reg == reg) { if (powerup)
ac97->power_up |= (1 << i); else
ac97->power_up &= ~(1 << i); break;
}
}
}
if (ac97_is_power_save_mode(ac97) && !powerup) /* adjust power-down bits after two seconds delay * (for avoiding loud click noises for many (OSS) apps * that open/close frequently)
*/
schedule_delayed_work(&ac97->power_work, secs_to_jiffies(power_save)); else {
cancel_delayed_work(&ac97->power_work);
update_power_regs(ac97);
}
staticvoid update_power_regs(struct snd_ac97 *ac97)
{ unsignedint power_up, bits; int i;
power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
power_up |= (1 << PWIDX_MIC); if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
power_up |= (1 << PWIDX_SURR); if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
power_up |= (1 << PWIDX_CLFE); #ifdef CONFIG_SND_AC97_POWER_SAVE if (ac97_is_power_save_mode(ac97))
power_up = ac97->power_up; #endif if (power_up) { if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) { /* needs power-up analog mix and vref */
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR3, 0);
msleep(1);
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR2, 0);
}
} for (i = 0; i < PWIDX_SIZE; i++) { if (power_up & (1 << i))
bits = 0; else
bits = power_regs[i].mask;
snd_ac97_update_bits(ac97, power_regs[i].power_reg,
power_regs[i].mask, bits);
} if (! power_up) { if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) { /* power down analog mix and vref */
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR2, AC97_PD_PR2);
snd_ac97_update_bits(ac97, AC97_POWERDOWN,
AC97_PD_PR3, AC97_PD_PR3);
}
}
}
#ifdef CONFIG_PM /** * snd_ac97_suspend - General suspend function for AC97 codec * @ac97: the ac97 instance * * Suspends the codec, power down the chip.
*/ void snd_ac97_suspend(struct snd_ac97 *ac97)
{ if (! ac97) return; if (ac97->build_ops->suspend)
ac97->build_ops->suspend(ac97); #ifdef CONFIG_SND_AC97_POWER_SAVE
cancel_delayed_work_sync(&ac97->power_work); #endif
snd_ac97_powerdown(ac97);
}
EXPORT_SYMBOL(snd_ac97_suspend);
/* * restore ac97 status
*/ staticvoid snd_ac97_restore_status(struct snd_ac97 *ac97)
{ int i;
for (i = 2; i < 0x7c ; i += 2) { if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID) continue; /* restore only accessible registers * some chip (e.g. nm256) may hang up when unsupported registers * are accessed..!
*/ if (test_bit(i, ac97->reg_accessed)) {
snd_ac97_write(ac97, i, ac97->regs[i]);
snd_ac97_read(ac97, i);
}
}
}
/* * restore IEC958 status
*/ staticvoid snd_ac97_restore_iec958(struct snd_ac97 *ac97)
{ if (ac97->ext_id & AC97_EI_SPDIF) { if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) { /* reset spdif status */
snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
snd_ac97_write(ac97, AC97_EXTENDED_STATUS, ac97->regs[AC97_EXTENDED_STATUS]); if (ac97->flags & AC97_CS_SPDIF)
snd_ac97_write(ac97, AC97_CSR_SPDIF, ac97->regs[AC97_CSR_SPDIF]); else
snd_ac97_write(ac97, AC97_SPDIF, ac97->regs[AC97_SPDIF]);
snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
}
}
}
/** * snd_ac97_resume - General resume function for AC97 codec * @ac97: the ac97 instance * * Do the standard resume procedure, power up and restoring the * old register values.
*/ void snd_ac97_resume(struct snd_ac97 *ac97)
{ unsignedlong end_time;
if (! ac97) return;
if (ac97->bus->ops->reset) {
ac97->bus->ops->reset(ac97); goto __reset_ready;
}
if (suffix) { if (snprintf(dst, msize, "%s %s", src, suffix) >= msize)
pr_warn("ALSA: AC97 control name '%s %s' truncated to '%s'\n",
src, suffix, dst);
} else { if (strscpy(dst, src, msize) < 0)
pr_warn("ALSA: AC97 control name '%s' truncated to '%s'\n",
src, dst);
}
}
/* remove the control with the given name and optional suffix */ staticint snd_ac97_remove_ctl(struct snd_ac97 *ac97, constchar *name, constchar *suffix)
{ struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
set_ctl_name(id.name, name, suffix);
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(ac97->bus->card, &id);
}
/* rename the control with the given name and optional suffix */ staticint snd_ac97_rename_ctl(struct snd_ac97 *ac97, constchar *src, constchar *dst, constchar *suffix)
{ struct snd_kcontrol *kctl = ctl_find(ac97, src, suffix); char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
/* ac97 tune: set up mic sharing for AD codecs */ staticint tune_ad_sharing(struct snd_ac97 *ac97)
{ unsignedshort scfg; if ((ac97->id & 0xffffff00) != 0x41445300) {
ac97_err(ac97, "ac97_quirk AD_SHARING is only for AD codecs\n"); return -EINVAL;
} /* Turn on OMS bit to route microphone to back panel */
scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x0200); return 0;
}
/* apply the quirk with the given type */ staticint apply_quirk(struct snd_ac97 *ac97, int type)
{ if (type <= 0) return 0; elseif (type >= ARRAY_SIZE(applicable_quirks)) return -EINVAL; if (applicable_quirks[type].func) return applicable_quirks[type].func(ac97); return 0;
}
/* apply the quirk with the given name */ staticint apply_quirk_str(struct snd_ac97 *ac97, constchar *typestr)
{ int i; conststruct quirk_table *q;
for (i = 0; i < ARRAY_SIZE(applicable_quirks); i++) {
q = &applicable_quirks[i]; if (q->name && ! strcmp(typestr, q->name)) return apply_quirk(ac97, i);
} /* for compatibility, accept the numbers, too */ if (*typestr >= '0' && *typestr <= '9') return apply_quirk(ac97, (int)simple_strtoul(typestr, NULL, 10)); return -EINVAL;
}
/** * snd_ac97_tune_hardware - tune up the hardware * @ac97: the ac97 instance * @quirk: quirk list * @override: explicit quirk value (overrides the list if non-NULL) * * Do some workaround for each pci device, such as renaming of the * headphone (true line-out) control as "Master". * The quirk-list must be terminated with a zero-filled entry. * * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_tune_hardware(struct snd_ac97 *ac97, conststruct ac97_quirk *quirk, constchar *override)
{ int result;
/* quirk overriden? */ if (override && strcmp(override, "-1") && strcmp(override, "default")) {
result = apply_quirk_str(ac97, override); if (result < 0)
ac97_err(ac97, "applying quirk type %s failed (%d)\n",
override, result); return result;
}
if (! quirk) return -EINVAL;
for (; quirk->subvendor; quirk++) { if (quirk->subvendor != ac97->subsystem_vendor)
continue;
if ((! quirk->mask && quirk->subdevice == ac97->subsystem_device) ||
quirk->subdevice == (quirk->mask & ac97->subsystem_device)) {
if (quirk->codec_id && quirk->codec_id != ac97->id)
continue;
ac97_dbg(ac97, "ac97 quirk for %s (%04x:%04x)\n",
quirk->name, ac97->subsystem_vendor,
ac97->subsystem_device);
result = apply_quirk(ac97, quirk->type);
if (result < 0)
ac97_err(ac97,
"applying quirk type %d for %s failed (%d)\n",
quirk->type, quirk->name, result);
return result;
}
}
return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.