// 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,
};
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.