xref: /openbmc/linux/sound/ppc/awacs.c (revision b268c34e)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * PMac AWACS lowlevel functions
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (c) by Takashi Iwai <tiwai@suse.de>
51da177e4SLinus Torvalds  * code based on dmasound.c.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
81da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
91da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
101da177e4SLinus Torvalds  *   (at your option) any later version.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
131da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
141da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
151da177e4SLinus Torvalds  *   GNU General Public License for more details.
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
181da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
191da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds 
236cbbfe1cSTakashi Iwai #include <linux/io.h>
241da177e4SLinus Torvalds #include <asm/nvram.h>
251da177e4SLinus Torvalds #include <linux/init.h>
261da177e4SLinus Torvalds #include <linux/delay.h>
271da177e4SLinus Torvalds #include <linux/slab.h>
281da177e4SLinus Torvalds #include <sound/core.h>
291da177e4SLinus Torvalds #include "pmac.h"
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds #ifdef CONFIG_ADB_CUDA
331da177e4SLinus Torvalds #define PMAC_AMP_AVAIL
341da177e4SLinus Torvalds #endif
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
3765b29f50STakashi Iwai struct awacs_amp {
381da177e4SLinus Torvalds 	unsigned char amp_master;
391da177e4SLinus Torvalds 	unsigned char amp_vol[2][2];
401da177e4SLinus Torvalds 	unsigned char amp_tone[2];
4165b29f50STakashi Iwai };
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA)
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 
4865b29f50STakashi Iwai static void snd_pmac_screamer_wait(struct snd_pmac *chip)
491da177e4SLinus Torvalds {
501da177e4SLinus Torvalds 	long timeout = 2000;
511da177e4SLinus Torvalds 	while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) {
521da177e4SLinus Torvalds 		mdelay(1);
531da177e4SLinus Torvalds 		if (! --timeout) {
541da177e4SLinus Torvalds 			snd_printd("snd_pmac_screamer_wait timeout\n");
551da177e4SLinus Torvalds 			break;
561da177e4SLinus Torvalds 		}
571da177e4SLinus Torvalds 	}
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds /*
611da177e4SLinus Torvalds  * write AWACS register
621da177e4SLinus Torvalds  */
631da177e4SLinus Torvalds static void
6465b29f50STakashi Iwai snd_pmac_awacs_write(struct snd_pmac *chip, int val)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds 	long timeout = 5000000;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER)
691da177e4SLinus Torvalds 		snd_pmac_screamer_wait(chip);
701da177e4SLinus Torvalds 	out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22));
711da177e4SLinus Torvalds 	while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) {
721da177e4SLinus Torvalds 		if (! --timeout) {
731da177e4SLinus Torvalds 			snd_printd("snd_pmac_awacs_write timeout\n");
741da177e4SLinus Torvalds 			break;
751da177e4SLinus Torvalds 		}
761da177e4SLinus Torvalds 	}
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds static void
8065b29f50STakashi Iwai snd_pmac_awacs_write_reg(struct snd_pmac *chip, int reg, int val)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds 	snd_pmac_awacs_write(chip, val | (reg << 12));
831da177e4SLinus Torvalds 	chip->awacs_reg[reg] = val;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds static void
8765b29f50STakashi Iwai snd_pmac_awacs_write_noreg(struct snd_pmac *chip, int reg, int val)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	snd_pmac_awacs_write(chip, val | (reg << 12));
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
928c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
931da177e4SLinus Torvalds /* Recalibrate chip */
9465b29f50STakashi Iwai static void screamer_recalibrate(struct snd_pmac *chip)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds 	if (chip->model != PMAC_SCREAMER)
971da177e4SLinus Torvalds 		return;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	/* Sorry for the horrible delays... I hope to get that improved
1001da177e4SLinus Torvalds 	 * by making the whole PM process asynchronous in a future version
1011da177e4SLinus Torvalds 	 */
1021da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
1031da177e4SLinus Torvalds 	if (chip->manufacturer == 0x1)
1041da177e4SLinus Torvalds 		/* delay for broken crystal part */
105989a0b24SNishanth Aravamudan 		msleep(750);
1061da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1,
10765b29f50STakashi Iwai 				   chip->awacs_reg[1] | MASK_RECALIBRATE |
10865b29f50STakashi Iwai 				   MASK_CMUTE | MASK_AMUTE);
1091da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
1101da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds #else
1141da177e4SLinus Torvalds #define screamer_recalibrate(chip) /* NOP */
1151da177e4SLinus Torvalds #endif
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds /*
1191da177e4SLinus Torvalds  * additional callback to set the pcm format
1201da177e4SLinus Torvalds  */
12165b29f50STakashi Iwai static void snd_pmac_awacs_set_format(struct snd_pmac *chip)
1221da177e4SLinus Torvalds {
1231da177e4SLinus Torvalds 	chip->awacs_reg[1] &= ~MASK_SAMPLERATE;
1241da177e4SLinus Torvalds 	chip->awacs_reg[1] |= chip->rate_index << 3;
1251da177e4SLinus Torvalds 	snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds /*
1301da177e4SLinus Torvalds  * AWACS volume callbacks
1311da177e4SLinus Torvalds  */
1321da177e4SLinus Torvalds /*
1331da177e4SLinus Torvalds  * volumes: 0-15 stereo
1341da177e4SLinus Torvalds  */
13565b29f50STakashi Iwai static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol,
13665b29f50STakashi Iwai 				      struct snd_ctl_elem_info *uinfo)
1371da177e4SLinus Torvalds {
1381da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1391da177e4SLinus Torvalds 	uinfo->count = 2;
1401da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
1411da177e4SLinus Torvalds 	uinfo->value.integer.max = 15;
1421da177e4SLinus Torvalds 	return 0;
1431da177e4SLinus Torvalds }
1441da177e4SLinus Torvalds 
14565b29f50STakashi Iwai static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol,
14665b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
1471da177e4SLinus Torvalds {
14865b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1491da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
1501da177e4SLinus Torvalds 	int lshift = (kcontrol->private_value >> 8) & 0xff;
1511da177e4SLinus Torvalds 	int inverted = (kcontrol->private_value >> 16) & 1;
1521da177e4SLinus Torvalds 	unsigned long flags;
1531da177e4SLinus Torvalds 	int vol[2];
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1561da177e4SLinus Torvalds 	vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf;
1571da177e4SLinus Torvalds 	vol[1] = chip->awacs_reg[reg] & 0xf;
1581da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1591da177e4SLinus Torvalds 	if (inverted) {
1601da177e4SLinus Torvalds 		vol[0] = 0x0f - vol[0];
1611da177e4SLinus Torvalds 		vol[1] = 0x0f - vol[1];
1621da177e4SLinus Torvalds 	}
1631da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = vol[0];
1641da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = vol[1];
1651da177e4SLinus Torvalds 	return 0;
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds 
16865b29f50STakashi Iwai static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
16965b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
1701da177e4SLinus Torvalds {
17165b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1721da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
1731da177e4SLinus Torvalds 	int lshift = (kcontrol->private_value >> 8) & 0xff;
1741da177e4SLinus Torvalds 	int inverted = (kcontrol->private_value >> 16) & 1;
1751da177e4SLinus Torvalds 	int val, oldval;
1761da177e4SLinus Torvalds 	unsigned long flags;
177d4079ac4STakashi Iwai 	unsigned int vol[2];
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	vol[0] = ucontrol->value.integer.value[0];
1801da177e4SLinus Torvalds 	vol[1] = ucontrol->value.integer.value[1];
181d4079ac4STakashi Iwai 	if (vol[0] > 0x0f || vol[1] > 0x0f)
182d4079ac4STakashi Iwai 		return -EINVAL;
1831da177e4SLinus Torvalds 	if (inverted) {
1841da177e4SLinus Torvalds 		vol[0] = 0x0f - vol[0];
1851da177e4SLinus Torvalds 		vol[1] = 0x0f - vol[1];
1861da177e4SLinus Torvalds 	}
1871da177e4SLinus Torvalds 	vol[0] &= 0x0f;
1881da177e4SLinus Torvalds 	vol[1] &= 0x0f;
1891da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1901da177e4SLinus Torvalds 	oldval = chip->awacs_reg[reg];
1911da177e4SLinus Torvalds 	val = oldval & ~(0xf | (0xf << lshift));
1921da177e4SLinus Torvalds 	val |= vol[0] << lshift;
1931da177e4SLinus Torvalds 	val |= vol[1];
1941da177e4SLinus Torvalds 	if (oldval != val)
1951da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, reg, val);
1961da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1971da177e4SLinus Torvalds 	return oldval != reg;
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds #define AWACS_VOLUME(xname, xreg, xshift, xinverted) \
2021da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
2031da177e4SLinus Torvalds   .info = snd_pmac_awacs_info_volume, \
2041da177e4SLinus Torvalds   .get = snd_pmac_awacs_get_volume, \
2051da177e4SLinus Torvalds   .put = snd_pmac_awacs_put_volume, \
2061da177e4SLinus Torvalds   .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) }
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds /*
2091da177e4SLinus Torvalds  * mute master/ogain for AWACS: mono
2101da177e4SLinus Torvalds  */
21165b29f50STakashi Iwai static int snd_pmac_awacs_get_switch(struct snd_kcontrol *kcontrol,
21265b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
2131da177e4SLinus Torvalds {
21465b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
2151da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
2161da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
2171da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 16) & 1;
2181da177e4SLinus Torvalds 	int val;
2191da177e4SLinus Torvalds 	unsigned long flags;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
2221da177e4SLinus Torvalds 	val = (chip->awacs_reg[reg] >> shift) & 1;
2231da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
2241da177e4SLinus Torvalds 	if (invert)
2251da177e4SLinus Torvalds 		val = 1 - val;
2261da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
2271da177e4SLinus Torvalds 	return 0;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
23065b29f50STakashi Iwai static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol,
23165b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
2321da177e4SLinus Torvalds {
23365b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
2341da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
2351da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
2361da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 16) & 1;
2371da177e4SLinus Torvalds 	int mask = 1 << shift;
2381da177e4SLinus Torvalds 	int val, changed;
2391da177e4SLinus Torvalds 	unsigned long flags;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
2421da177e4SLinus Torvalds 	val = chip->awacs_reg[reg] & ~mask;
2431da177e4SLinus Torvalds 	if (ucontrol->value.integer.value[0] != invert)
2441da177e4SLinus Torvalds 		val |= mask;
2451da177e4SLinus Torvalds 	changed = chip->awacs_reg[reg] != val;
2461da177e4SLinus Torvalds 	if (changed)
2471da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, reg, val);
2481da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
2491da177e4SLinus Torvalds 	return changed;
2501da177e4SLinus Torvalds }
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds #define AWACS_SWITCH(xname, xreg, xshift, xinvert) \
2531da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
2541da177e4SLinus Torvalds   .info = snd_pmac_boolean_mono_info, \
2551da177e4SLinus Torvalds   .get = snd_pmac_awacs_get_switch, \
2561da177e4SLinus Torvalds   .put = snd_pmac_awacs_put_switch, \
2571da177e4SLinus Torvalds   .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) }
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
2611da177e4SLinus Torvalds /*
2621da177e4SLinus Torvalds  * controls for perch/whisper extension cards, e.g. G3 desktop
2631da177e4SLinus Torvalds  *
2641da177e4SLinus Torvalds  * TDA7433 connected via i2c address 0x45 (= 0x8a),
2651da177e4SLinus Torvalds  * accessed through cuda
2661da177e4SLinus Torvalds  */
2671da177e4SLinus Torvalds static void awacs_set_cuda(int reg, int val)
2681da177e4SLinus Torvalds {
2691da177e4SLinus Torvalds 	struct adb_request req;
2707ae44cfaSRisto Suominen 	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a,
2717ae44cfaSRisto Suominen 			reg, val);
2721da177e4SLinus Torvalds 	while (! req.complete)
2731da177e4SLinus Torvalds 		cuda_poll();
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds /*
2771da177e4SLinus Torvalds  * level = 0 - 14, 7 = 0 dB
2781da177e4SLinus Torvalds  */
27965b29f50STakashi Iwai static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble)
2801da177e4SLinus Torvalds {
2811da177e4SLinus Torvalds 	amp->amp_tone[0] = bass;
2821da177e4SLinus Torvalds 	amp->amp_tone[1] = treble;
2831da177e4SLinus Torvalds 	if (bass > 7)
2841da177e4SLinus Torvalds 		bass = (14 - bass) + 8;
2851da177e4SLinus Torvalds 	if (treble > 7)
2861da177e4SLinus Torvalds 		treble = (14 - treble) + 8;
2871da177e4SLinus Torvalds 	awacs_set_cuda(2, (bass << 4) | treble);
2881da177e4SLinus Torvalds }
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds /*
2911da177e4SLinus Torvalds  * vol = 0 - 31 (attenuation), 32 = mute bit, stereo
2921da177e4SLinus Torvalds  */
2937ae44cfaSRisto Suominen static int awacs_amp_set_vol(struct awacs_amp *amp, int index,
2947ae44cfaSRisto Suominen 			     int lvol, int rvol, int do_check)
2951da177e4SLinus Torvalds {
2961da177e4SLinus Torvalds 	if (do_check && amp->amp_vol[index][0] == lvol &&
2971da177e4SLinus Torvalds 			amp->amp_vol[index][1] == rvol)
2981da177e4SLinus Torvalds 		return 0;
2991da177e4SLinus Torvalds 	awacs_set_cuda(3 + index, lvol);
3001da177e4SLinus Torvalds 	awacs_set_cuda(5 + index, rvol);
3011da177e4SLinus Torvalds 	amp->amp_vol[index][0] = lvol;
3021da177e4SLinus Torvalds 	amp->amp_vol[index][1] = rvol;
3031da177e4SLinus Torvalds 	return 1;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds /*
3071da177e4SLinus Torvalds  * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB
3081da177e4SLinus Torvalds  */
30965b29f50STakashi Iwai static void awacs_amp_set_master(struct awacs_amp *amp, int vol)
3101da177e4SLinus Torvalds {
3111da177e4SLinus Torvalds 	amp->amp_master = vol;
3121da177e4SLinus Torvalds 	if (vol <= 79)
3131da177e4SLinus Torvalds 		vol = 32 + (79 - vol);
3141da177e4SLinus Torvalds 	else
3151da177e4SLinus Torvalds 		vol = 32 - (vol - 79);
3161da177e4SLinus Torvalds 	awacs_set_cuda(1, vol);
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds 
31965b29f50STakashi Iwai static void awacs_amp_free(struct snd_pmac *chip)
3201da177e4SLinus Torvalds {
32165b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3225e246b85STakashi Iwai 	if (!amp)
3235e246b85STakashi Iwai 		return;
3241da177e4SLinus Torvalds 	kfree(amp);
3251da177e4SLinus Torvalds 	chip->mixer_data = NULL;
3261da177e4SLinus Torvalds 	chip->mixer_free = NULL;
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds /*
3311da177e4SLinus Torvalds  * mixer controls
3321da177e4SLinus Torvalds  */
33365b29f50STakashi Iwai static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol,
33465b29f50STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
3351da177e4SLinus Torvalds {
3361da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3371da177e4SLinus Torvalds 	uinfo->count = 2;
3381da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3391da177e4SLinus Torvalds 	uinfo->value.integer.max = 31;
3401da177e4SLinus Torvalds 	return 0;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
34365b29f50STakashi Iwai static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol,
34465b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3451da177e4SLinus Torvalds {
34665b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3471da177e4SLinus Torvalds 	int index = kcontrol->private_value;
34865b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3495e246b85STakashi Iwai 
3501da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31);
3511da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31);
3521da177e4SLinus Torvalds 	return 0;
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds 
35565b29f50STakashi Iwai static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol,
35665b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3571da177e4SLinus Torvalds {
35865b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3591da177e4SLinus Torvalds 	int index = kcontrol->private_value;
3601da177e4SLinus Torvalds 	int vol[2];
36165b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3621da177e4SLinus Torvalds 
3637ae44cfaSRisto Suominen 	vol[0] = (31 - (ucontrol->value.integer.value[0] & 31))
3647ae44cfaSRisto Suominen 		| (amp->amp_vol[index][0] & 32);
3657ae44cfaSRisto Suominen 	vol[1] = (31 - (ucontrol->value.integer.value[1] & 31))
3667ae44cfaSRisto Suominen 		| (amp->amp_vol[index][1] & 32);
3671da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
3681da177e4SLinus Torvalds }
3691da177e4SLinus Torvalds 
37065b29f50STakashi Iwai static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol,
37165b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3721da177e4SLinus Torvalds {
37365b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3741da177e4SLinus Torvalds 	int index = kcontrol->private_value;
37565b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3765e246b85STakashi Iwai 
3777ae44cfaSRisto Suominen 	ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32)
3787ae44cfaSRisto Suominen 					? 0 : 1;
3797ae44cfaSRisto Suominen 	ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32)
3807ae44cfaSRisto Suominen 					? 0 : 1;
3811da177e4SLinus Torvalds 	return 0;
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds 
38465b29f50STakashi Iwai static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol,
38565b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3861da177e4SLinus Torvalds {
38765b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3881da177e4SLinus Torvalds 	int index = kcontrol->private_value;
3891da177e4SLinus Torvalds 	int vol[2];
39065b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3911da177e4SLinus Torvalds 
3927ae44cfaSRisto Suominen 	vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32)
3937ae44cfaSRisto Suominen 		| (amp->amp_vol[index][0] & 31);
3947ae44cfaSRisto Suominen 	vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32)
3957ae44cfaSRisto Suominen 		| (amp->amp_vol[index][1] & 31);
3961da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds 
39965b29f50STakashi Iwai static int snd_pmac_awacs_info_tone_amp(struct snd_kcontrol *kcontrol,
40065b29f50STakashi Iwai 					struct snd_ctl_elem_info *uinfo)
4011da177e4SLinus Torvalds {
4021da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4031da177e4SLinus Torvalds 	uinfo->count = 1;
4041da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
4051da177e4SLinus Torvalds 	uinfo->value.integer.max = 14;
4061da177e4SLinus Torvalds 	return 0;
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds 
40965b29f50STakashi Iwai static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol,
41065b29f50STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
4111da177e4SLinus Torvalds {
41265b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
4131da177e4SLinus Torvalds 	int index = kcontrol->private_value;
41465b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
4155e246b85STakashi Iwai 
4161da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = amp->amp_tone[index];
4171da177e4SLinus Torvalds 	return 0;
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
42065b29f50STakashi Iwai static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol,
42165b29f50STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
4221da177e4SLinus Torvalds {
42365b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
4241da177e4SLinus Torvalds 	int index = kcontrol->private_value;
42565b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
426d4079ac4STakashi Iwai 	unsigned int val;
4275e246b85STakashi Iwai 
428d4079ac4STakashi Iwai 	val = ucontrol->value.integer.value[0];
429d4079ac4STakashi Iwai 	if (val > 14)
430d4079ac4STakashi Iwai 		return -EINVAL;
431d4079ac4STakashi Iwai 	if (val != amp->amp_tone[index]) {
432d4079ac4STakashi Iwai 		amp->amp_tone[index] = val;
4331da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
4341da177e4SLinus Torvalds 		return 1;
4351da177e4SLinus Torvalds 	}
4361da177e4SLinus Torvalds 	return 0;
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds 
43965b29f50STakashi Iwai static int snd_pmac_awacs_info_master_amp(struct snd_kcontrol *kcontrol,
44065b29f50STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
4411da177e4SLinus Torvalds {
4421da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4431da177e4SLinus Torvalds 	uinfo->count = 1;
4441da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
4451da177e4SLinus Torvalds 	uinfo->value.integer.max = 99;
4461da177e4SLinus Torvalds 	return 0;
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds 
44965b29f50STakashi Iwai static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol,
45065b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
4511da177e4SLinus Torvalds {
45265b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
45365b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
4545e246b85STakashi Iwai 
4551da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = amp->amp_master;
4561da177e4SLinus Torvalds 	return 0;
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds 
45965b29f50STakashi Iwai static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
46065b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
4611da177e4SLinus Torvalds {
46265b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
46365b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
464d4079ac4STakashi Iwai 	unsigned int val;
4655e246b85STakashi Iwai 
466d4079ac4STakashi Iwai 	val = ucontrol->value.integer.value[0];
467d4079ac4STakashi Iwai 	if (val > 99)
468d4079ac4STakashi Iwai 		return -EINVAL;
469d4079ac4STakashi Iwai 	if (val != amp->amp_master) {
470d4079ac4STakashi Iwai 		amp->amp_master = val;
4711da177e4SLinus Torvalds 		awacs_amp_set_master(amp, amp->amp_master);
4721da177e4SLinus Torvalds 		return 1;
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds 	return 0;
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds #define AMP_CH_SPK	0
4781da177e4SLinus Torvalds #define AMP_CH_HD	1
4791da177e4SLinus Torvalds 
48015afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] = {
4811da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
482ad1cd745SJaroslav Kysela 	  .name = "Speaker Playback Volume",
4831da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_volume_amp,
4841da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_volume_amp,
4851da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_volume_amp,
4861da177e4SLinus Torvalds 	  .private_value = AMP_CH_SPK,
4871da177e4SLinus Torvalds 	},
4881da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4891da177e4SLinus Torvalds 	  .name = "Headphone Playback Volume",
4901da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_volume_amp,
4911da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_volume_amp,
4921da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_volume_amp,
4931da177e4SLinus Torvalds 	  .private_value = AMP_CH_HD,
4941da177e4SLinus Torvalds 	},
4951da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4961da177e4SLinus Torvalds 	  .name = "Tone Control - Bass",
4971da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_tone_amp,
4981da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_tone_amp,
4991da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_tone_amp,
5001da177e4SLinus Torvalds 	  .private_value = 0,
5011da177e4SLinus Torvalds 	},
5021da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5031da177e4SLinus Torvalds 	  .name = "Tone Control - Treble",
5041da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_tone_amp,
5051da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_tone_amp,
5061da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_tone_amp,
5071da177e4SLinus Torvalds 	  .private_value = 1,
5081da177e4SLinus Torvalds 	},
5091da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5101da177e4SLinus Torvalds 	  .name = "Amp Master Playback Volume",
5111da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_master_amp,
5121da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_master_amp,
5131da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_master_amp,
5141da177e4SLinus Torvalds 	},
5151da177e4SLinus Torvalds };
5161da177e4SLinus Torvalds 
51715afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = {
5181da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5191da177e4SLinus Torvalds 	.name = "Headphone Playback Switch",
5201da177e4SLinus Torvalds 	.info = snd_pmac_boolean_stereo_info,
5211da177e4SLinus Torvalds 	.get = snd_pmac_awacs_get_switch_amp,
5221da177e4SLinus Torvalds 	.put = snd_pmac_awacs_put_switch_amp,
5231da177e4SLinus Torvalds 	.private_value = AMP_CH_HD,
5241da177e4SLinus Torvalds };
5251da177e4SLinus Torvalds 
52615afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw = {
5271da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
528ad1cd745SJaroslav Kysela 	.name = "Speaker Playback Switch",
5291da177e4SLinus Torvalds 	.info = snd_pmac_boolean_stereo_info,
5301da177e4SLinus Torvalds 	.get = snd_pmac_awacs_get_switch_amp,
5311da177e4SLinus Torvalds 	.put = snd_pmac_awacs_put_switch_amp,
5321da177e4SLinus Torvalds 	.private_value = AMP_CH_SPK,
5331da177e4SLinus Torvalds };
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds /*
5391da177e4SLinus Torvalds  * mic boost for screamer
5401da177e4SLinus Torvalds  */
54165b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol,
54265b29f50STakashi Iwai 					    struct snd_ctl_elem_info *uinfo)
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
5451da177e4SLinus Torvalds 	uinfo->count = 1;
5461da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
547a8c2a6bfSRisto Suominen 	uinfo->value.integer.max = 3;
5481da177e4SLinus Torvalds 	return 0;
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds 
55165b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol,
55265b29f50STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
5531da177e4SLinus Torvalds {
55465b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
555a8c2a6bfSRisto Suominen 	int val = 0;
5561da177e4SLinus Torvalds 	unsigned long flags;
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
5591da177e4SLinus Torvalds 	if (chip->awacs_reg[6] & MASK_MIC_BOOST)
560a8c2a6bfSRisto Suominen 		val |= 2;
561a8c2a6bfSRisto Suominen 	if (chip->awacs_reg[0] & MASK_GAINLINE)
562a8c2a6bfSRisto Suominen 		val |= 1;
5631da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
5641da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
5651da177e4SLinus Torvalds 	return 0;
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds 
56865b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
56965b29f50STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
5701da177e4SLinus Torvalds {
57165b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
5721da177e4SLinus Torvalds 	int changed = 0;
5731da177e4SLinus Torvalds 	int val0, val6;
5741da177e4SLinus Torvalds 	unsigned long flags;
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
5771da177e4SLinus Torvalds 	val0 = chip->awacs_reg[0] & ~MASK_GAINLINE;
5781da177e4SLinus Torvalds 	val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST;
579a8c2a6bfSRisto Suominen 	if (ucontrol->value.integer.value[0] & 1)
5801da177e4SLinus Torvalds 		val0 |= MASK_GAINLINE;
581a8c2a6bfSRisto Suominen 	if (ucontrol->value.integer.value[0] & 2)
5821da177e4SLinus Torvalds 		val6 |= MASK_MIC_BOOST;
5831da177e4SLinus Torvalds 	if (val0 != chip->awacs_reg[0]) {
5841da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 0, val0);
5851da177e4SLinus Torvalds 		changed = 1;
5861da177e4SLinus Torvalds 	}
5871da177e4SLinus Torvalds 	if (val6 != chip->awacs_reg[6]) {
5881da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 6, val6);
5891da177e4SLinus Torvalds 		changed = 1;
5901da177e4SLinus Torvalds 	}
5911da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
5921da177e4SLinus Torvalds 	return changed;
5931da177e4SLinus Torvalds }
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds /*
5961da177e4SLinus Torvalds  * lists of mixer elements
5971da177e4SLinus Torvalds  */
59815afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mixers[] = {
5991da177e4SLinus Torvalds 	AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
600a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Capture Volume", 0, 4, 0),
601a8c2a6bfSRisto Suominen /*	AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */
602a8c2a6bfSRisto Suominen };
603a8c2a6bfSRisto Suominen 
60415afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] = {
605a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
606a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
607a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
608a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
609a8c2a6bfSRisto Suominen };
610a8c2a6bfSRisto Suominen 
61115afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] = {
612a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
613dca7c741SRisto Suominen };
614dca7c741SRisto Suominen 
61515afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] = {
616dca7c741SRisto Suominen 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
617a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
618a8c2a6bfSRisto Suominen };
619a8c2a6bfSRisto Suominen 
62015afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] = {
6214dbf95baSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
6224dbf95baSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
6234dbf95baSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
6244dbf95baSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
6254dbf95baSRisto Suominen };
6264dbf95baSRisto Suominen 
62715afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] = {
628a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
629a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
630a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
631a8c2a6bfSRisto Suominen };
632a8c2a6bfSRisto Suominen 
63315afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] = {
634dca7c741SRisto Suominen 	AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1),
635dca7c741SRisto Suominen };
636dca7c741SRisto Suominen 
63715afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] = {
638a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
6391da177e4SLinus Torvalds 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
6401da177e4SLinus Torvalds };
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds /* FIXME: is this correct order?
6431da177e4SLinus Torvalds  * screamer (powerbook G3 pismo) seems to have different bits...
6441da177e4SLinus Torvalds  */
64515afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] = {
6461da177e4SLinus Torvalds 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
6471da177e4SLinus Torvalds 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
6481da177e4SLinus Torvalds };
6491da177e4SLinus Torvalds 
65015afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] = {
6511da177e4SLinus Torvalds 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
6521da177e4SLinus Torvalds 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
6531da177e4SLinus Torvalds };
6541da177e4SLinus Torvalds 
65515afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] = {
656dca7c741SRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
657dca7c741SRisto Suominen };
658dca7c741SRisto Suominen 
65915afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_master_sw =
6601da177e4SLinus Torvalds AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
6611da177e4SLinus Torvalds 
66215afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac =
663a8c2a6bfSRisto Suominen AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
664a8c2a6bfSRisto Suominen 
66515afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 =
666dca7c741SRisto Suominen AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1);
667dca7c741SRisto Suominen 
66815afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] = {
669a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
6701da177e4SLinus Torvalds };
6711da177e4SLinus Torvalds 
67215afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] = {
6731da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
674a8c2a6bfSRisto Suominen 	  .name = "Mic Boost Capture Volume",
6751da177e4SLinus Torvalds 	  .info = snd_pmac_screamer_mic_boost_info,
6761da177e4SLinus Torvalds 	  .get = snd_pmac_screamer_mic_boost_get,
6771da177e4SLinus Torvalds 	  .put = snd_pmac_screamer_mic_boost_put,
6781da177e4SLinus Torvalds 	},
6791da177e4SLinus Torvalds };
6801da177e4SLinus Torvalds 
68115afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] =
682a8c2a6bfSRisto Suominen {
683a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
684a8c2a6bfSRisto Suominen };
685a8c2a6bfSRisto Suominen 
68615afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] =
687a8c2a6bfSRisto Suominen {
688a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
689a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
690a8c2a6bfSRisto Suominen };
691a8c2a6bfSRisto Suominen 
69215afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] =
693a8c2a6bfSRisto Suominen {
694a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
695a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
696a8c2a6bfSRisto Suominen };
697a8c2a6bfSRisto Suominen 
69815afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] = {
699ad1cd745SJaroslav Kysela 	AWACS_VOLUME("Speaker Playback Volume", 4, 6, 1),
7001da177e4SLinus Torvalds };
701a8c2a6bfSRisto Suominen 
70215afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw =
703ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
7041da177e4SLinus Torvalds 
70515afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 =
706ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
707030b655bSRisto Suominen 
70815afafc2SBill Pemberton static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 =
709ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
710a8c2a6bfSRisto Suominen 
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds /*
7131da177e4SLinus Torvalds  * add new mixer elements to the card
7141da177e4SLinus Torvalds  */
7157ae44cfaSRisto Suominen static int build_mixers(struct snd_pmac *chip, int nums,
7167ae44cfaSRisto Suominen 			struct snd_kcontrol_new *mixers)
7171da177e4SLinus Torvalds {
7181da177e4SLinus Torvalds 	int i, err;
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds 	for (i = 0; i < nums; i++) {
7217ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip));
7227ae44cfaSRisto Suominen 		if (err < 0)
7231da177e4SLinus Torvalds 			return err;
7241da177e4SLinus Torvalds 	}
7251da177e4SLinus Torvalds 	return 0;
7261da177e4SLinus Torvalds }
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds /*
7301da177e4SLinus Torvalds  * restore all registers
7311da177e4SLinus Torvalds  */
73265b29f50STakashi Iwai static void awacs_restore_all_regs(struct snd_pmac *chip)
7331da177e4SLinus Torvalds {
7341da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
7351da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
7361da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]);
7371da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]);
7381da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
7391da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]);
7401da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
7411da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]);
7421da177e4SLinus Torvalds 	}
7431da177e4SLinus Torvalds }
7441da177e4SLinus Torvalds 
7458c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
74665b29f50STakashi Iwai static void snd_pmac_awacs_suspend(struct snd_pmac *chip)
7471da177e4SLinus Torvalds {
7481da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1]
7491da177e4SLinus Torvalds 					     | MASK_AMUTE | MASK_CMUTE));
7501da177e4SLinus Torvalds }
7511da177e4SLinus Torvalds 
75265b29f50STakashi Iwai static void snd_pmac_awacs_resume(struct snd_pmac *chip)
7531da177e4SLinus Torvalds {
75471a157e8SGrant Likely 	if (of_machine_is_compatible("PowerBook3,1")
75571a157e8SGrant Likely 	    || of_machine_is_compatible("PowerBook3,2")) {
756989a0b24SNishanth Aravamudan 		msleep(100);
7571da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 1,
7581da177e4SLinus Torvalds 			chip->awacs_reg[1] & ~MASK_PAROUT);
759989a0b24SNishanth Aravamudan 		msleep(300);
7601da177e4SLinus Torvalds 	}
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 	awacs_restore_all_regs(chip);
7631da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
7641da177e4SLinus Torvalds 		/* reset power bits in reg 6 */
7651da177e4SLinus Torvalds 		mdelay(5);
7661da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
7671da177e4SLinus Torvalds 	}
7681da177e4SLinus Torvalds 	screamer_recalibrate(chip);
7691da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
7701da177e4SLinus Torvalds 	if (chip->mixer_data) {
77165b29f50STakashi Iwai 		struct awacs_amp *amp = chip->mixer_data;
7727ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 0,
7737ae44cfaSRisto Suominen 				  amp->amp_vol[0][0], amp->amp_vol[0][1], 0);
7747ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 1,
7757ae44cfaSRisto Suominen 				  amp->amp_vol[1][0], amp->amp_vol[1][1], 0);
7761da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
7771da177e4SLinus Torvalds 		awacs_amp_set_master(amp, amp->amp_master);
7781da177e4SLinus Torvalds 	}
7791da177e4SLinus Torvalds #endif
7801da177e4SLinus Torvalds }
7818c870933SBenjamin Herrenschmidt #endif /* CONFIG_PM */
7821da177e4SLinus Torvalds 
78371a157e8SGrant Likely #define IS_PM7500 (of_machine_is_compatible("AAPL,7500") \
78471a157e8SGrant Likely 		|| of_machine_is_compatible("AAPL,8500") \
78571a157e8SGrant Likely 		|| of_machine_is_compatible("AAPL,9500"))
78671a157e8SGrant Likely #define IS_PM5500 (of_machine_is_compatible("AAPL,e411"))
78771a157e8SGrant Likely #define IS_BEIGE (of_machine_is_compatible("AAPL,Gossamer"))
78871a157e8SGrant Likely #define IS_IMAC1 (of_machine_is_compatible("PowerMac2,1"))
78971a157e8SGrant Likely #define IS_IMAC2 (of_machine_is_compatible("PowerMac2,2") \
79071a157e8SGrant Likely 		|| of_machine_is_compatible("PowerMac4,1"))
79171a157e8SGrant Likely #define IS_G4AGP (of_machine_is_compatible("PowerMac3,1"))
79271a157e8SGrant Likely #define IS_LOMBARD (of_machine_is_compatible("PowerBook1,1"))
793a8c2a6bfSRisto Suominen 
794030b655bSRisto Suominen static int imac1, imac2;
795a8c2a6bfSRisto Suominen 
7961da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE
7971da177e4SLinus Torvalds /*
7981da177e4SLinus Torvalds  * auto-mute stuffs
7991da177e4SLinus Torvalds  */
80065b29f50STakashi Iwai static int snd_pmac_awacs_detect_headphone(struct snd_pmac *chip)
8011da177e4SLinus Torvalds {
8021da177e4SLinus Torvalds 	return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0;
8031da177e4SLinus Torvalds }
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
80665b29f50STakashi Iwai static int toggle_amp_mute(struct awacs_amp *amp, int index, int mute)
8071da177e4SLinus Torvalds {
8081da177e4SLinus Torvalds 	int vol[2];
8091da177e4SLinus Torvalds 	vol[0] = amp->amp_vol[index][0] & 31;
8101da177e4SLinus Torvalds 	vol[1] = amp->amp_vol[index][1] & 31;
8111da177e4SLinus Torvalds 	if (mute) {
8121da177e4SLinus Torvalds 		vol[0] |= 32;
8131da177e4SLinus Torvalds 		vol[1] |= 32;
8141da177e4SLinus Torvalds 	}
8151da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
8161da177e4SLinus Torvalds }
8171da177e4SLinus Torvalds #endif
8181da177e4SLinus Torvalds 
81965b29f50STakashi Iwai static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)
8201da177e4SLinus Torvalds {
8211da177e4SLinus Torvalds 	if (chip->auto_mute) {
8221da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
8231da177e4SLinus Torvalds 		if (chip->mixer_data) {
82465b29f50STakashi Iwai 			struct awacs_amp *amp = chip->mixer_data;
8251da177e4SLinus Torvalds 			int changed;
8261da177e4SLinus Torvalds 			if (snd_pmac_awacs_detect_headphone(chip)) {
8271da177e4SLinus Torvalds 				changed = toggle_amp_mute(amp, AMP_CH_HD, 0);
8281da177e4SLinus Torvalds 				changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1);
8291da177e4SLinus Torvalds 			} else {
8301da177e4SLinus Torvalds 				changed = toggle_amp_mute(amp, AMP_CH_HD, 1);
8311da177e4SLinus Torvalds 				changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0);
8321da177e4SLinus Torvalds 			}
8331da177e4SLinus Torvalds 			if (do_notify && ! changed)
8341da177e4SLinus Torvalds 				return;
8351da177e4SLinus Torvalds 		} else
8361da177e4SLinus Torvalds #endif
8371da177e4SLinus Torvalds 		{
838a8c2a6bfSRisto Suominen 			int reg = chip->awacs_reg[1]
839a8c2a6bfSRisto Suominen 				| (MASK_HDMUTE | MASK_SPKMUTE);
840030b655bSRisto Suominen 			if (imac1) {
841030b655bSRisto Suominen 				reg &= ~MASK_SPKMUTE;
842030b655bSRisto Suominen 				reg |= MASK_PAROUT1;
843030b655bSRisto Suominen 			} else if (imac2) {
844a8c2a6bfSRisto Suominen 				reg &= ~MASK_SPKMUTE;
845a8c2a6bfSRisto Suominen 				reg &= ~MASK_PAROUT1;
846a8c2a6bfSRisto Suominen 			}
8471da177e4SLinus Torvalds 			if (snd_pmac_awacs_detect_headphone(chip))
8481da177e4SLinus Torvalds 				reg &= ~MASK_HDMUTE;
849030b655bSRisto Suominen 			else if (imac1)
850030b655bSRisto Suominen 				reg &= ~MASK_PAROUT1;
851030b655bSRisto Suominen 			else if (imac2)
852a8c2a6bfSRisto Suominen 				reg |= MASK_PAROUT1;
8531da177e4SLinus Torvalds 			else
8541da177e4SLinus Torvalds 				reg &= ~MASK_SPKMUTE;
8551da177e4SLinus Torvalds 			if (do_notify && reg == chip->awacs_reg[1])
8561da177e4SLinus Torvalds 				return;
8571da177e4SLinus Torvalds 			snd_pmac_awacs_write_reg(chip, 1, reg);
8581da177e4SLinus Torvalds 		}
8591da177e4SLinus Torvalds 		if (do_notify) {
8601da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8611da177e4SLinus Torvalds 				       &chip->master_sw_ctl->id);
8621da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8631da177e4SLinus Torvalds 				       &chip->speaker_sw_ctl->id);
8641da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8651da177e4SLinus Torvalds 				       &chip->hp_detect_ctl->id);
8661da177e4SLinus Torvalds 		}
8671da177e4SLinus Torvalds 	}
8681da177e4SLinus Torvalds }
8691da177e4SLinus Torvalds #endif /* PMAC_SUPPORT_AUTOMUTE */
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 
8721da177e4SLinus Torvalds /*
8731da177e4SLinus Torvalds  * initialize chip
8741da177e4SLinus Torvalds  */
87515afafc2SBill Pemberton int
87665b29f50STakashi Iwai snd_pmac_awacs_init(struct snd_pmac *chip)
8771da177e4SLinus Torvalds {
878a8c2a6bfSRisto Suominen 	int pm7500 = IS_PM7500;
879b0a8a8fdSRisto Suominen 	int pm5500 = IS_PM5500;
880a8c2a6bfSRisto Suominen 	int beige = IS_BEIGE;
8814dbf95baSRisto Suominen 	int g4agp = IS_G4AGP;
882573934bcSRisto Suominen 	int lombard = IS_LOMBARD;
883030b655bSRisto Suominen 	int imac;
8841da177e4SLinus Torvalds 	int err, vol;
885dca7c741SRisto Suominen 	struct snd_kcontrol *vmaster_sw, *vmaster_vol;
886dca7c741SRisto Suominen 	struct snd_kcontrol *master_vol, *speaker_vol;
8871da177e4SLinus Torvalds 
888030b655bSRisto Suominen 	imac1 = IS_IMAC1;
889030b655bSRisto Suominen 	imac2 = IS_IMAC2;
890030b655bSRisto Suominen 	imac = imac1 || imac2;
8911da177e4SLinus Torvalds 	/* looks like MASK_GAINLINE triggers something, so we set here
8921da177e4SLinus Torvalds 	 * as start-up
8931da177e4SLinus Torvalds 	 */
8941da177e4SLinus Torvalds 	chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE;
8951da177e4SLinus Torvalds 	chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE;
8961da177e4SLinus Torvalds 	/* FIXME: Only machines with external SRS module need MASK_PAROUT */
8971da177e4SLinus Torvalds 	if (chip->has_iic || chip->device_id == 0x5 ||
8981da177e4SLinus Torvalds 	    /* chip->_device_id == 0x8 || */
8991da177e4SLinus Torvalds 	    chip->device_id == 0xb)
9001da177e4SLinus Torvalds 		chip->awacs_reg[1] |= MASK_PAROUT;
9011da177e4SLinus Torvalds 	/* get default volume from nvram */
9021da177e4SLinus Torvalds 	// vol = (~nvram_read_byte(0x1308) & 7) << 1;
9031da177e4SLinus Torvalds 	// vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
9041da177e4SLinus Torvalds 	vol = 0x0f; /* no, on alsa, muted as default */
9051da177e4SLinus Torvalds 	vol = vol + (vol << 6);
9061da177e4SLinus Torvalds 	chip->awacs_reg[2] = vol;
9071da177e4SLinus Torvalds 	chip->awacs_reg[4] = vol;
9081da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
9097ae44cfaSRisto Suominen 		/* FIXME: screamer has loopthru vol control */
9107ae44cfaSRisto Suominen 		chip->awacs_reg[5] = vol;
9117ae44cfaSRisto Suominen 		/* FIXME: maybe should be vol << 3 for PCMCIA speaker */
9127ae44cfaSRisto Suominen 		chip->awacs_reg[6] = MASK_MIC_BOOST;
9131da177e4SLinus Torvalds 		chip->awacs_reg[7] = 0;
9141da177e4SLinus Torvalds 	}
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds 	awacs_restore_all_regs(chip);
9171da177e4SLinus Torvalds 	chip->manufacturer = (in_le32(&chip->awacs->codec_stat) >> 8) & 0xf;
9181da177e4SLinus Torvalds 	screamer_recalibrate(chip);
9191da177e4SLinus Torvalds 
9201da177e4SLinus Torvalds 	chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;
9211da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
9221da177e4SLinus Torvalds 	if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) {
92359feddb2SPanagiotis Issaris 		struct awacs_amp *amp = kzalloc(sizeof(*amp), GFP_KERNEL);
9241da177e4SLinus Torvalds 		if (! amp)
9251da177e4SLinus Torvalds 			return -ENOMEM;
9261da177e4SLinus Torvalds 		chip->mixer_data = amp;
9271da177e4SLinus Torvalds 		chip->mixer_free = awacs_amp_free;
9287ae44cfaSRisto Suominen 		/* mute and zero vol */
9297ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 0, 63, 63, 0);
9301da177e4SLinus Torvalds 		awacs_amp_set_vol(amp, 1, 63, 63, 0);
9311da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, 7, 7); /* 0 dB */
9321da177e4SLinus Torvalds 		awacs_amp_set_master(amp, 79); /* 0 dB */
9331da177e4SLinus Torvalds 	}
9341da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	if (chip->hp_stat_mask == 0) {
9371da177e4SLinus Torvalds 		/* set headphone-jack detection bit */
9381da177e4SLinus Torvalds 		switch (chip->model) {
9391da177e4SLinus Torvalds 		case PMAC_AWACS:
940b0a8a8fdSRisto Suominen 			chip->hp_stat_mask = pm7500 || pm5500 ? MASK_HDPCONN
941a8c2a6bfSRisto Suominen 				: MASK_LOCONN;
9421da177e4SLinus Torvalds 			break;
9431da177e4SLinus Torvalds 		case PMAC_SCREAMER:
9441da177e4SLinus Torvalds 			switch (chip->device_id) {
9451da177e4SLinus Torvalds 			case 0x08:
946a8c2a6bfSRisto Suominen 			case 0x0B:
947a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = imac
948a8c2a6bfSRisto Suominen 					? MASK_LOCONN_IMAC |
949a8c2a6bfSRisto Suominen 					MASK_HDPLCONN_IMAC |
950a8c2a6bfSRisto Suominen 					MASK_HDPRCONN_IMAC
951a8c2a6bfSRisto Suominen 					: MASK_HDPCONN;
9521da177e4SLinus Torvalds 				break;
9531da177e4SLinus Torvalds 			case 0x00:
9541da177e4SLinus Torvalds 			case 0x05:
955a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = MASK_LOCONN;
9561da177e4SLinus Torvalds 				break;
9571da177e4SLinus Torvalds 			default:
958a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = MASK_HDPCONN;
9591da177e4SLinus Torvalds 				break;
9601da177e4SLinus Torvalds 			}
9611da177e4SLinus Torvalds 			break;
9621da177e4SLinus Torvalds 		default:
9631da177e4SLinus Torvalds 			snd_BUG();
9641da177e4SLinus Torvalds 			break;
9651da177e4SLinus Torvalds 		}
9661da177e4SLinus Torvalds 	}
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds 	/*
9691da177e4SLinus Torvalds 	 * build mixers
9701da177e4SLinus Torvalds 	 */
9711da177e4SLinus Torvalds 	strcpy(chip->card->mixername, "PowerMac AWACS");
9721da177e4SLinus Torvalds 
9737ae44cfaSRisto Suominen 	err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers),
9747ae44cfaSRisto Suominen 				snd_pmac_awacs_mixers);
9757ae44cfaSRisto Suominen 	if (err < 0)
9761da177e4SLinus Torvalds 		return err;
9774dbf95baSRisto Suominen 	if (beige || g4agp)
978a8c2a6bfSRisto Suominen 		;
979b0a8a8fdSRisto Suominen 	else if (chip->model == PMAC_SCREAMER || pm5500)
9801da177e4SLinus Torvalds 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2),
9811da177e4SLinus Torvalds 				   snd_pmac_screamer_mixers2);
982a8c2a6bfSRisto Suominen 	else if (!pm7500)
9831da177e4SLinus Torvalds 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2),
9841da177e4SLinus Torvalds 				   snd_pmac_awacs_mixers2);
9851da177e4SLinus Torvalds 	if (err < 0)
9861da177e4SLinus Torvalds 		return err;
987dca7c741SRisto Suominen 	if (pm5500) {
988dca7c741SRisto Suominen 		err = build_mixers(chip,
989dca7c741SRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers2_pmac5500),
990dca7c741SRisto Suominen 				   snd_pmac_awacs_mixers2_pmac5500);
991dca7c741SRisto Suominen 		if (err < 0)
992dca7c741SRisto Suominen 			return err;
993dca7c741SRisto Suominen 	}
994b268c34eSArnd Bergmann 	master_vol = NULL;
995a8c2a6bfSRisto Suominen 	if (pm7500)
996a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
997a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
998a8c2a6bfSRisto Suominen 				   snd_pmac_awacs_mixers_pmac7500);
999dca7c741SRisto Suominen 	else if (pm5500)
1000dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
1001dca7c741SRisto Suominen 		    (master_vol = snd_ctl_new1(snd_pmac_awacs_mixers_pmac5500,
1002dca7c741SRisto Suominen 						chip)));
1003a8c2a6bfSRisto Suominen 	else if (beige)
1004a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1005a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_beige),
1006a8c2a6bfSRisto Suominen 				   snd_pmac_screamer_mixers_beige);
1007dca7c741SRisto Suominen 	else if (imac || lombard) {
1008dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
1009dca7c741SRisto Suominen 		    (master_vol = snd_ctl_new1(snd_pmac_screamer_mixers_lo,
1010dca7c741SRisto Suominen 						chip)));
1011dca7c741SRisto Suominen 		if (err < 0)
1012dca7c741SRisto Suominen 			return err;
1013a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1014a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_imac),
1015a8c2a6bfSRisto Suominen 				   snd_pmac_screamer_mixers_imac);
1016dca7c741SRisto Suominen 	} else if (g4agp)
10174dbf95baSRisto Suominen 		err = build_mixers(chip,
10184dbf95baSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_g4agp),
10194dbf95baSRisto Suominen 				   snd_pmac_screamer_mixers_g4agp);
1020a8c2a6bfSRisto Suominen 	else
1021a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1022a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac),
1023a8c2a6bfSRisto Suominen 				   snd_pmac_awacs_mixers_pmac);
1024a8c2a6bfSRisto Suominen 	if (err < 0)
1025a8c2a6bfSRisto Suominen 		return err;
1026573934bcSRisto Suominen 	chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp || lombard)
1027a8c2a6bfSRisto Suominen 			? &snd_pmac_awacs_master_sw_imac
1028dca7c741SRisto Suominen 			: pm5500
1029dca7c741SRisto Suominen 			? &snd_pmac_awacs_master_sw_pmac5500
1030a8c2a6bfSRisto Suominen 			: &snd_pmac_awacs_master_sw, chip);
10317ae44cfaSRisto Suominen 	err = snd_ctl_add(chip->card, chip->master_sw_ctl);
10327ae44cfaSRisto Suominen 	if (err < 0)
10331da177e4SLinus Torvalds 		return err;
10341da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
10351da177e4SLinus Torvalds 	if (chip->mixer_data) {
10361da177e4SLinus Torvalds 		/* use amplifier.  the signal is connected from route A
10371da177e4SLinus Torvalds 		 * to the amp.  the amp has its headphone and speaker
10381da177e4SLinus Torvalds 		 * volumes and mute switches, so we use them instead of
10391da177e4SLinus Torvalds 		 * screamer registers.
10401da177e4SLinus Torvalds 		 * in this case, it seems the route C is not used.
10411da177e4SLinus Torvalds 		 */
10427ae44cfaSRisto Suominen 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol),
10437ae44cfaSRisto Suominen 					snd_pmac_awacs_amp_vol);
10447ae44cfaSRisto Suominen 		if (err < 0)
10451da177e4SLinus Torvalds 			return err;
10461da177e4SLinus Torvalds 		/* overwrite */
10477ae44cfaSRisto Suominen 		chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw,
10487ae44cfaSRisto Suominen 							chip);
10497ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->master_sw_ctl);
10507ae44cfaSRisto Suominen 		if (err < 0)
10511da177e4SLinus Torvalds 			return err;
10527ae44cfaSRisto Suominen 		chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw,
10537ae44cfaSRisto Suominen 							chip);
10547ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
10557ae44cfaSRisto Suominen 		if (err < 0)
10561da177e4SLinus Torvalds 			return err;
10571da177e4SLinus Torvalds 	} else
10581da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
10591da177e4SLinus Torvalds 	{
10601da177e4SLinus Torvalds 		/* route A = headphone, route C = speaker */
1061dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
1062dca7c741SRisto Suominen 		    (speaker_vol = snd_ctl_new1(snd_pmac_awacs_speaker_vol,
1063dca7c741SRisto Suominen 						chip)));
10647ae44cfaSRisto Suominen 		if (err < 0)
10651da177e4SLinus Torvalds 			return err;
1066030b655bSRisto Suominen 		chip->speaker_sw_ctl = snd_ctl_new1(imac1
1067030b655bSRisto Suominen 				? &snd_pmac_awacs_speaker_sw_imac1
1068030b655bSRisto Suominen 				: imac2
1069030b655bSRisto Suominen 				? &snd_pmac_awacs_speaker_sw_imac2
1070a8c2a6bfSRisto Suominen 				: &snd_pmac_awacs_speaker_sw, chip);
10717ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
10727ae44cfaSRisto Suominen 		if (err < 0)
10731da177e4SLinus Torvalds 			return err;
10741da177e4SLinus Torvalds 	}
10751da177e4SLinus Torvalds 
1076dca7c741SRisto Suominen 	if (pm5500 || imac || lombard) {
1077dca7c741SRisto Suominen 		vmaster_sw = snd_ctl_make_virtual_master(
1078dca7c741SRisto Suominen 			"Master Playback Switch", (unsigned int *) NULL);
1079dca7c741SRisto Suominen 		err = snd_ctl_add_slave_uncached(vmaster_sw,
1080dca7c741SRisto Suominen 						 chip->master_sw_ctl);
1081dca7c741SRisto Suominen 		if (err < 0)
1082dca7c741SRisto Suominen 			return err;
1083dca7c741SRisto Suominen 		err = snd_ctl_add_slave_uncached(vmaster_sw,
1084dca7c741SRisto Suominen 						  chip->speaker_sw_ctl);
1085dca7c741SRisto Suominen 		if (err < 0)
1086dca7c741SRisto Suominen 			return err;
1087dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card, vmaster_sw);
1088dca7c741SRisto Suominen 		if (err < 0)
1089dca7c741SRisto Suominen 			return err;
1090dca7c741SRisto Suominen 		vmaster_vol = snd_ctl_make_virtual_master(
1091dca7c741SRisto Suominen 			"Master Playback Volume", (unsigned int *) NULL);
1092dca7c741SRisto Suominen 		err = snd_ctl_add_slave(vmaster_vol, master_vol);
1093dca7c741SRisto Suominen 		if (err < 0)
1094dca7c741SRisto Suominen 			return err;
1095dca7c741SRisto Suominen 		err = snd_ctl_add_slave(vmaster_vol, speaker_vol);
1096dca7c741SRisto Suominen 		if (err < 0)
1097dca7c741SRisto Suominen 			return err;
1098dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card, vmaster_vol);
1099dca7c741SRisto Suominen 		if (err < 0)
1100dca7c741SRisto Suominen 			return err;
1101dca7c741SRisto Suominen 	}
1102dca7c741SRisto Suominen 
11034dbf95baSRisto Suominen 	if (beige || g4agp)
1104a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1105a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige),
1106a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost_beige);
1107a8c2a6bfSRisto Suominen 	else if (imac)
1108a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1109a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost_imac),
1110a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost_imac);
1111a8c2a6bfSRisto Suominen 	else if (chip->model == PMAC_SCREAMER)
1112a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1113a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost),
1114a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost);
1115a8c2a6bfSRisto Suominen 	else if (pm7500)
1116a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1117a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_awacs_mic_boost_pmac7500),
1118a8c2a6bfSRisto Suominen 				snd_pmac_awacs_mic_boost_pmac7500);
1119a8c2a6bfSRisto Suominen 	else
1120a8c2a6bfSRisto Suominen 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost),
1121a8c2a6bfSRisto Suominen 				snd_pmac_awacs_mic_boost);
1122a8c2a6bfSRisto Suominen 	if (err < 0)
11231da177e4SLinus Torvalds 		return err;
11241da177e4SLinus Torvalds 
11251da177e4SLinus Torvalds 	/*
11261da177e4SLinus Torvalds 	 * set lowlevel callbacks
11271da177e4SLinus Torvalds 	 */
11281da177e4SLinus Torvalds 	chip->set_format = snd_pmac_awacs_set_format;
11298c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
11301da177e4SLinus Torvalds 	chip->suspend = snd_pmac_awacs_suspend;
11311da177e4SLinus Torvalds 	chip->resume = snd_pmac_awacs_resume;
11321da177e4SLinus Torvalds #endif
11331da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE
11347ae44cfaSRisto Suominen 	err = snd_pmac_add_automute(chip);
11357ae44cfaSRisto Suominen 	if (err < 0)
11361da177e4SLinus Torvalds 		return err;
11371da177e4SLinus Torvalds 	chip->detect_headphone = snd_pmac_awacs_detect_headphone;
11381da177e4SLinus Torvalds 	chip->update_automute = snd_pmac_awacs_update_automute;
11391da177e4SLinus Torvalds 	snd_pmac_awacs_update_automute(chip, 0); /* update the status only */
11401da177e4SLinus Torvalds #endif
11411da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
11421da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
11431da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
11441da177e4SLinus Torvalds 	}
11451da177e4SLinus Torvalds 
11461da177e4SLinus Torvalds 	return 0;
11471da177e4SLinus Torvalds }
1148