// SPDX-License-Identifier: GPL-2.0-only /* * Apple Onboard Audio driver for tas codec * * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> * * Open questions: * - How to distinguish between 3004 and versions? * * FIXMEs: * - This codec driver doesn't honour the 'connected' * property of the aoa_codec struct, hence if * it is used in machines where not everything is * connected it will display wrong mixer elements. * - Driver assumes that the microphone is always * monaureal and connected to the right channel of * the input. This should also be a codec-dependent * flag, maybe the codec should have 3 different * bits for the three different possibilities how * it can be hooked up... * But as long as I don't see any hardware hooked * up that way... * - As Apple notes in their code, the tas3004 seems * to delay the right channel by one sample. You can * see this when for example recording stereo in * audacity, or recording the tas output via cable * on another machine (use a sinus generator or so). * I tried programming the BiQuads but couldn't * make the delay work, maybe someone can read the * datasheet and fix it. The relevant Apple comment * is in AppleTAS3004Audio.cpp lines 1637 ff. Note * that their comment describing how they program * the filters sucks... * * Other things: * - this should actually register *two* aoa_codec * structs since it has two inputs. Then it must * use the prepare callback to forbid running the * secondary output on a different clock. * Also, whatever bus knows how to do this must * provide two soundbus_dev devices and the fabric * must be able to link them correctly. * * I don't even know if Apple ever uses the second * port on the tas3004 though, I don't think their * i2s controllers can even do it. OTOH, they all * derive the clocks from common clocks, so it * might just be possible. The framework allows the * codec to refine the transfer_info items in the * usable callback, so we can simply remove the * rates the second instance is not using when it * actually is in use. * Maybe we'll need to make the sound busses have * a 'clock group id' value so the codec can * determine if the two outputs can be driven at * the same time. But that is likely overkill, up * to the fabric to not link them up incorrectly, * and up to the hardware designer to not wire * them up in some weird unusable way.
*/ #include <linux/i2c.h> #include <asm/pmac_low_i2c.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/slab.h>
MODULE_AUTHOR("Johannes Berg ");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("tas codec driver for snd-aoa");
staticvoid tas_set_volume(struct tas *tas)
{
u8 block[6]; int tmp;
u8 left, right;
left = tas->cached_volume_l;
right = tas->cached_volume_r;
if (left > 177) left = 177; if (right > 177) right = 177;
if (tas->mute_l) left = 0; if (tas->mute_r) right = 0;
/* analysing the volume and mixer tables shows * that they are similar enough when we shift * the mixer table down by 4 bits. The error * is miniscule, in just one item the error * is 1, at a value of 0x07f17b (mixer table
* value is 0x07f17a) */
tmp = tas_gaintable[left];
block[0] = tmp>>20;
block[1] = tmp>>12;
block[2] = tmp>>4;
tmp = tas_gaintable[right];
block[3] = tmp>>20;
block[4] = tmp>>12;
block[5] = tmp>>4;
tas_write_reg(tas, TAS_REG_VOL, 6, block);
}
staticvoid tas_set_mixer(struct tas *tas)
{
u8 block[9]; int tmp, i;
u8 val;
for (i=0;i<3;i++) {
val = tas->mixer_l[i]; if (val > 177) val = 177;
tmp = tas_gaintable[val];
block[3*i+0] = tmp>>16;
block[3*i+1] = tmp>>8;
block[3*i+2] = tmp;
}
tas_write_reg(tas, TAS_REG_LMIX, 9, block);
for (i=0;i<3;i++) {
val = tas->mixer_r[i]; if (val > 177) val = 177;
tmp = tas_gaintable[val];
block[3*i+0] = tmp>>16;
block[3*i+1] = tmp>>8;
block[3*i+2] = tmp;
}
tas_write_reg(tas, TAS_REG_RMIX, 9, block);
}
staticint tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct tas *tas = snd_kcontrol_chip(kcontrol); int oldacr;
if (ucontrol->value.enumerated.item[0] > 1) return -EINVAL;
mutex_lock(&tas->mtx);
oldacr = tas->acr;
/* * Despite what the data sheet says in one place, the * TAS_ACR_B_MONAUREAL bit forces mono output even when * input A (line in) is selected.
*/
tas->acr &= ~(TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL); if (ucontrol->value.enumerated.item[0])
tas->acr |= TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL |
TAS_ACR_B_MON_SEL_RIGHT; if (oldacr == tas->acr) {
mutex_unlock(&tas->mtx); return 0;
} if (tas->hw_enabled)
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
mutex_unlock(&tas->mtx); return 1;
}
staticconststruct snd_kcontrol_new capture_source_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* If we name this 'Input Source', it properly shows up in * alsamixer as a selection, * but it's shown under the * 'Playback' category. * If I name it 'Capture Source', it shows up in strange * ways (two bools of which one can be selected at a * time) but at least it's shown in the 'Capture' * category. * I was told that this was due to backward compatibility, * but I don't understand then why the mangling is *not* * done when I name it "Input Source".....
*/
.name = "Capture Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = tas_snd_capture_source_info,
.get = tas_snd_capture_source_get,
.put = tas_snd_capture_source_put,
};
switch(clock) { case CLOCK_SWITCH_PREPARE_SLAVE: /* Clocks are going away, mute mute mute */
tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
tas->hw_enabled = 0; break; case CLOCK_SWITCH_SLAVE: /* Clocks are back, re-init the codec */
mutex_lock(&tas->mtx);
tas_reset_init(tas);
tas_set_volume(tas);
tas_set_mixer(tas);
tas->hw_enabled = 1;
tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
mutex_unlock(&tas->mtx); break; default: /* doesn't happen as of now */ return -EINVAL;
} return 0;
}
#ifdef CONFIG_PM /* we are controlled via i2c and assume that is always up * If that wasn't the case, we'd have to suspend once
* our i2c device is suspended, and then take note of that! */ staticint tas_suspend(struct tas *tas)
{
mutex_lock(&tas->mtx);
tas->hw_enabled = 0;
tas->acr |= TAS_ACR_ANALOG_PDOWN;
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
mutex_unlock(&tas->mtx); return 0;
}
staticstruct codec_info tas_codec_info = {
.transfers = tas_transfers, /* in theory, we can drive it at 512 too... * but so far the framework doesn't allow
* for that and I don't see much point in it. */
.sysclock_factor = 256, /* same here, could be 32 for just one 16 bit format */
.bus_factor = 64,
.owner = THIS_MODULE,
.usable = tas_usable,
.switch_clock = tas_switch_clock,
.suspend = _tas_suspend,
.resume = _tas_resume,
};
staticint tas_init_codec(struct aoa_codec *codec)
{ struct tas *tas = codec_to_tas(codec); int err;
if (!tas->codec.gpio || !tas->codec.gpio->methods) {
printk(KERN_ERR PFX "gpios not assigned!!\n"); return -EINVAL;
}
mutex_lock(&tas->mtx); if (tas_reset_init(tas)) {
printk(KERN_ERR PFX "tas failed to initialise\n");
mutex_unlock(&tas->mtx); return -ENXIO;
}
tas->hw_enabled = 1;
mutex_unlock(&tas->mtx);
if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
aoa_get_card(),
&tas_codec_info, tas)) {
printk(KERN_ERR PFX "error attaching tas to soundbus\n"); return -ENODEV;
}
if (aoa_snd_device_new(SNDRV_DEV_CODEC, tas, &ops)) {
printk(KERN_ERR PFX "failed to create tas snd device!\n"); return -ENODEV;
}
err = aoa_snd_ctl_add(snd_ctl_new1(&volume_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&mute_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&pcm1_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&monitor_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&capture_source_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&drc_range_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&drc_switch_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&treble_control, tas)); if (err) goto error;
err = aoa_snd_ctl_add(snd_ctl_new1(&bass_control, tas)); if (err) goto error;
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.