xref: /openbmc/linux/sound/ppc/awacs.c (revision 7e9f2839)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * PMac AWACS lowlevel functions
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (c) by Takashi Iwai <tiwai@suse.de>
61da177e4SLinus Torvalds  * code based on dmasound.c.
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds 
106cbbfe1cSTakashi Iwai #include <linux/io.h>
111da177e4SLinus Torvalds #include <asm/nvram.h>
121da177e4SLinus Torvalds #include <linux/init.h>
131da177e4SLinus Torvalds #include <linux/delay.h>
14*7e9f2839SRob Herring #include <linux/of.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
161da177e4SLinus Torvalds #include <sound/core.h>
171da177e4SLinus Torvalds #include "pmac.h"
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #ifdef CONFIG_ADB_CUDA
211da177e4SLinus Torvalds #define PMAC_AMP_AVAIL
221da177e4SLinus Torvalds #endif
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
2565b29f50STakashi Iwai struct awacs_amp {
261da177e4SLinus Torvalds 	unsigned char amp_master;
271da177e4SLinus Torvalds 	unsigned char amp_vol[2][2];
281da177e4SLinus Torvalds 	unsigned char amp_tone[2];
2965b29f50STakashi Iwai };
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA)
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 
snd_pmac_screamer_wait(struct snd_pmac * chip)3665b29f50STakashi Iwai static void snd_pmac_screamer_wait(struct snd_pmac *chip)
371da177e4SLinus Torvalds {
381da177e4SLinus Torvalds 	long timeout = 2000;
391da177e4SLinus Torvalds 	while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) {
401da177e4SLinus Torvalds 		mdelay(1);
411da177e4SLinus Torvalds 		if (! --timeout) {
421da177e4SLinus Torvalds 			snd_printd("snd_pmac_screamer_wait timeout\n");
431da177e4SLinus Torvalds 			break;
441da177e4SLinus Torvalds 		}
451da177e4SLinus Torvalds 	}
461da177e4SLinus Torvalds }
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds /*
491da177e4SLinus Torvalds  * write AWACS register
501da177e4SLinus Torvalds  */
511da177e4SLinus Torvalds static void
snd_pmac_awacs_write(struct snd_pmac * chip,int val)5265b29f50STakashi Iwai snd_pmac_awacs_write(struct snd_pmac *chip, int val)
531da177e4SLinus Torvalds {
541da177e4SLinus Torvalds 	long timeout = 5000000;
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER)
571da177e4SLinus Torvalds 		snd_pmac_screamer_wait(chip);
581da177e4SLinus Torvalds 	out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22));
591da177e4SLinus Torvalds 	while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) {
601da177e4SLinus Torvalds 		if (! --timeout) {
611da177e4SLinus Torvalds 			snd_printd("snd_pmac_awacs_write timeout\n");
621da177e4SLinus Torvalds 			break;
631da177e4SLinus Torvalds 		}
641da177e4SLinus Torvalds 	}
651da177e4SLinus Torvalds }
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds static void
snd_pmac_awacs_write_reg(struct snd_pmac * chip,int reg,int val)6865b29f50STakashi Iwai snd_pmac_awacs_write_reg(struct snd_pmac *chip, int reg, int val)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds 	snd_pmac_awacs_write(chip, val | (reg << 12));
711da177e4SLinus Torvalds 	chip->awacs_reg[reg] = val;
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds static void
snd_pmac_awacs_write_noreg(struct snd_pmac * chip,int reg,int val)7565b29f50STakashi Iwai snd_pmac_awacs_write_noreg(struct snd_pmac *chip, int reg, int val)
761da177e4SLinus Torvalds {
771da177e4SLinus Torvalds 	snd_pmac_awacs_write(chip, val | (reg << 12));
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds 
808c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
811da177e4SLinus Torvalds /* Recalibrate chip */
screamer_recalibrate(struct snd_pmac * chip)8265b29f50STakashi Iwai static void screamer_recalibrate(struct snd_pmac *chip)
831da177e4SLinus Torvalds {
841da177e4SLinus Torvalds 	if (chip->model != PMAC_SCREAMER)
851da177e4SLinus Torvalds 		return;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	/* Sorry for the horrible delays... I hope to get that improved
881da177e4SLinus Torvalds 	 * by making the whole PM process asynchronous in a future version
891da177e4SLinus Torvalds 	 */
901da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
911da177e4SLinus Torvalds 	if (chip->manufacturer == 0x1)
921da177e4SLinus Torvalds 		/* delay for broken crystal part */
93989a0b24SNishanth Aravamudan 		msleep(750);
941da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1,
9565b29f50STakashi Iwai 				   chip->awacs_reg[1] | MASK_RECALIBRATE |
9665b29f50STakashi Iwai 				   MASK_CMUTE | MASK_AMUTE);
971da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
981da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds #else
1021da177e4SLinus Torvalds #define screamer_recalibrate(chip) /* NOP */
1031da177e4SLinus Torvalds #endif
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds /*
1071da177e4SLinus Torvalds  * additional callback to set the pcm format
1081da177e4SLinus Torvalds  */
snd_pmac_awacs_set_format(struct snd_pmac * chip)10965b29f50STakashi Iwai static void snd_pmac_awacs_set_format(struct snd_pmac *chip)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds 	chip->awacs_reg[1] &= ~MASK_SAMPLERATE;
1121da177e4SLinus Torvalds 	chip->awacs_reg[1] |= chip->rate_index << 3;
1131da177e4SLinus Torvalds 	snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds /*
1181da177e4SLinus Torvalds  * AWACS volume callbacks
1191da177e4SLinus Torvalds  */
1201da177e4SLinus Torvalds /*
1211da177e4SLinus Torvalds  * volumes: 0-15 stereo
1221da177e4SLinus Torvalds  */
snd_pmac_awacs_info_volume(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)12365b29f50STakashi Iwai static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol,
12465b29f50STakashi Iwai 				      struct snd_ctl_elem_info *uinfo)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1271da177e4SLinus Torvalds 	uinfo->count = 2;
1281da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
1291da177e4SLinus Torvalds 	uinfo->value.integer.max = 15;
1301da177e4SLinus Torvalds 	return 0;
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
snd_pmac_awacs_get_volume(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)13365b29f50STakashi Iwai static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol,
13465b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
1351da177e4SLinus Torvalds {
13665b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1371da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
1381da177e4SLinus Torvalds 	int lshift = (kcontrol->private_value >> 8) & 0xff;
1391da177e4SLinus Torvalds 	int inverted = (kcontrol->private_value >> 16) & 1;
1401da177e4SLinus Torvalds 	unsigned long flags;
1411da177e4SLinus Torvalds 	int vol[2];
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1441da177e4SLinus Torvalds 	vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf;
1451da177e4SLinus Torvalds 	vol[1] = chip->awacs_reg[reg] & 0xf;
1461da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1471da177e4SLinus Torvalds 	if (inverted) {
1481da177e4SLinus Torvalds 		vol[0] = 0x0f - vol[0];
1491da177e4SLinus Torvalds 		vol[1] = 0x0f - vol[1];
1501da177e4SLinus Torvalds 	}
1511da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = vol[0];
1521da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = vol[1];
1531da177e4SLinus Torvalds 	return 0;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
snd_pmac_awacs_put_volume(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)15665b29f50STakashi Iwai static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
15765b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
1581da177e4SLinus Torvalds {
15965b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1601da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
1611da177e4SLinus Torvalds 	int lshift = (kcontrol->private_value >> 8) & 0xff;
1621da177e4SLinus Torvalds 	int inverted = (kcontrol->private_value >> 16) & 1;
1631da177e4SLinus Torvalds 	int val, oldval;
1641da177e4SLinus Torvalds 	unsigned long flags;
165d4079ac4STakashi Iwai 	unsigned int vol[2];
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	vol[0] = ucontrol->value.integer.value[0];
1681da177e4SLinus Torvalds 	vol[1] = ucontrol->value.integer.value[1];
169d4079ac4STakashi Iwai 	if (vol[0] > 0x0f || vol[1] > 0x0f)
170d4079ac4STakashi Iwai 		return -EINVAL;
1711da177e4SLinus Torvalds 	if (inverted) {
1721da177e4SLinus Torvalds 		vol[0] = 0x0f - vol[0];
1731da177e4SLinus Torvalds 		vol[1] = 0x0f - vol[1];
1741da177e4SLinus Torvalds 	}
1751da177e4SLinus Torvalds 	vol[0] &= 0x0f;
1761da177e4SLinus Torvalds 	vol[1] &= 0x0f;
1771da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1781da177e4SLinus Torvalds 	oldval = chip->awacs_reg[reg];
1791da177e4SLinus Torvalds 	val = oldval & ~(0xf | (0xf << lshift));
1801da177e4SLinus Torvalds 	val |= vol[0] << lshift;
1811da177e4SLinus Torvalds 	val |= vol[1];
1821da177e4SLinus Torvalds 	if (oldval != val)
1831da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, reg, val);
1841da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1851da177e4SLinus Torvalds 	return oldval != reg;
1861da177e4SLinus Torvalds }
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds #define AWACS_VOLUME(xname, xreg, xshift, xinverted) \
1901da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
1911da177e4SLinus Torvalds   .info = snd_pmac_awacs_info_volume, \
1921da177e4SLinus Torvalds   .get = snd_pmac_awacs_get_volume, \
1931da177e4SLinus Torvalds   .put = snd_pmac_awacs_put_volume, \
1941da177e4SLinus Torvalds   .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) }
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds /*
1971da177e4SLinus Torvalds  * mute master/ogain for AWACS: mono
1981da177e4SLinus Torvalds  */
snd_pmac_awacs_get_switch(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)19965b29f50STakashi Iwai static int snd_pmac_awacs_get_switch(struct snd_kcontrol *kcontrol,
20065b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
2011da177e4SLinus Torvalds {
20265b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
2031da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
2041da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
2051da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 16) & 1;
2061da177e4SLinus Torvalds 	int val;
2071da177e4SLinus Torvalds 	unsigned long flags;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
2101da177e4SLinus Torvalds 	val = (chip->awacs_reg[reg] >> shift) & 1;
2111da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
2121da177e4SLinus Torvalds 	if (invert)
2131da177e4SLinus Torvalds 		val = 1 - val;
2141da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
2151da177e4SLinus Torvalds 	return 0;
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
snd_pmac_awacs_put_switch(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)21865b29f50STakashi Iwai static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol,
21965b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
2201da177e4SLinus Torvalds {
22165b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
2221da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
2231da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
2241da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 16) & 1;
2251da177e4SLinus Torvalds 	int mask = 1 << shift;
2261da177e4SLinus Torvalds 	int val, changed;
2271da177e4SLinus Torvalds 	unsigned long flags;
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
2301da177e4SLinus Torvalds 	val = chip->awacs_reg[reg] & ~mask;
2311da177e4SLinus Torvalds 	if (ucontrol->value.integer.value[0] != invert)
2321da177e4SLinus Torvalds 		val |= mask;
2331da177e4SLinus Torvalds 	changed = chip->awacs_reg[reg] != val;
2341da177e4SLinus Torvalds 	if (changed)
2351da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, reg, val);
2361da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
2371da177e4SLinus Torvalds 	return changed;
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds #define AWACS_SWITCH(xname, xreg, xshift, xinvert) \
2411da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
2421da177e4SLinus Torvalds   .info = snd_pmac_boolean_mono_info, \
2431da177e4SLinus Torvalds   .get = snd_pmac_awacs_get_switch, \
2441da177e4SLinus Torvalds   .put = snd_pmac_awacs_put_switch, \
2451da177e4SLinus Torvalds   .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) }
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
2491da177e4SLinus Torvalds /*
2501da177e4SLinus Torvalds  * controls for perch/whisper extension cards, e.g. G3 desktop
2511da177e4SLinus Torvalds  *
2521da177e4SLinus Torvalds  * TDA7433 connected via i2c address 0x45 (= 0x8a),
2531da177e4SLinus Torvalds  * accessed through cuda
2541da177e4SLinus Torvalds  */
awacs_set_cuda(int reg,int val)2551da177e4SLinus Torvalds static void awacs_set_cuda(int reg, int val)
2561da177e4SLinus Torvalds {
2571da177e4SLinus Torvalds 	struct adb_request req;
2587ae44cfaSRisto Suominen 	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a,
2597ae44cfaSRisto Suominen 			reg, val);
2601da177e4SLinus Torvalds 	while (! req.complete)
2611da177e4SLinus Torvalds 		cuda_poll();
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds /*
2651da177e4SLinus Torvalds  * level = 0 - 14, 7 = 0 dB
2661da177e4SLinus Torvalds  */
awacs_amp_set_tone(struct awacs_amp * amp,int bass,int treble)26765b29f50STakashi Iwai static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble)
2681da177e4SLinus Torvalds {
2691da177e4SLinus Torvalds 	amp->amp_tone[0] = bass;
2701da177e4SLinus Torvalds 	amp->amp_tone[1] = treble;
2711da177e4SLinus Torvalds 	if (bass > 7)
2721da177e4SLinus Torvalds 		bass = (14 - bass) + 8;
2731da177e4SLinus Torvalds 	if (treble > 7)
2741da177e4SLinus Torvalds 		treble = (14 - treble) + 8;
2751da177e4SLinus Torvalds 	awacs_set_cuda(2, (bass << 4) | treble);
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds /*
2791da177e4SLinus Torvalds  * vol = 0 - 31 (attenuation), 32 = mute bit, stereo
2801da177e4SLinus Torvalds  */
awacs_amp_set_vol(struct awacs_amp * amp,int index,int lvol,int rvol,int do_check)2817ae44cfaSRisto Suominen static int awacs_amp_set_vol(struct awacs_amp *amp, int index,
2827ae44cfaSRisto Suominen 			     int lvol, int rvol, int do_check)
2831da177e4SLinus Torvalds {
2841da177e4SLinus Torvalds 	if (do_check && amp->amp_vol[index][0] == lvol &&
2851da177e4SLinus Torvalds 			amp->amp_vol[index][1] == rvol)
2861da177e4SLinus Torvalds 		return 0;
2871da177e4SLinus Torvalds 	awacs_set_cuda(3 + index, lvol);
2881da177e4SLinus Torvalds 	awacs_set_cuda(5 + index, rvol);
2891da177e4SLinus Torvalds 	amp->amp_vol[index][0] = lvol;
2901da177e4SLinus Torvalds 	amp->amp_vol[index][1] = rvol;
2911da177e4SLinus Torvalds 	return 1;
2921da177e4SLinus Torvalds }
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds /*
2951da177e4SLinus Torvalds  * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB
2961da177e4SLinus Torvalds  */
awacs_amp_set_master(struct awacs_amp * amp,int vol)29765b29f50STakashi Iwai static void awacs_amp_set_master(struct awacs_amp *amp, int vol)
2981da177e4SLinus Torvalds {
2991da177e4SLinus Torvalds 	amp->amp_master = vol;
3001da177e4SLinus Torvalds 	if (vol <= 79)
3011da177e4SLinus Torvalds 		vol = 32 + (79 - vol);
3021da177e4SLinus Torvalds 	else
3031da177e4SLinus Torvalds 		vol = 32 - (vol - 79);
3041da177e4SLinus Torvalds 	awacs_set_cuda(1, vol);
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds 
awacs_amp_free(struct snd_pmac * chip)30765b29f50STakashi Iwai static void awacs_amp_free(struct snd_pmac *chip)
3081da177e4SLinus Torvalds {
30965b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3105e246b85STakashi Iwai 	if (!amp)
3115e246b85STakashi Iwai 		return;
3121da177e4SLinus Torvalds 	kfree(amp);
3131da177e4SLinus Torvalds 	chip->mixer_data = NULL;
3141da177e4SLinus Torvalds 	chip->mixer_free = NULL;
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds /*
3191da177e4SLinus Torvalds  * mixer controls
3201da177e4SLinus Torvalds  */
snd_pmac_awacs_info_volume_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)32165b29f50STakashi Iwai static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol,
32265b29f50STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
3231da177e4SLinus Torvalds {
3241da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3251da177e4SLinus Torvalds 	uinfo->count = 2;
3261da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3271da177e4SLinus Torvalds 	uinfo->value.integer.max = 31;
3281da177e4SLinus Torvalds 	return 0;
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
snd_pmac_awacs_get_volume_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)33165b29f50STakashi Iwai static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol,
33265b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3331da177e4SLinus Torvalds {
33465b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3351da177e4SLinus Torvalds 	int index = kcontrol->private_value;
33665b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3375e246b85STakashi Iwai 
3381da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31);
3391da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31);
3401da177e4SLinus Torvalds 	return 0;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
snd_pmac_awacs_put_volume_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)34365b29f50STakashi Iwai static int snd_pmac_awacs_put_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;
3481da177e4SLinus Torvalds 	int vol[2];
34965b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3501da177e4SLinus Torvalds 
3517ae44cfaSRisto Suominen 	vol[0] = (31 - (ucontrol->value.integer.value[0] & 31))
3527ae44cfaSRisto Suominen 		| (amp->amp_vol[index][0] & 32);
3537ae44cfaSRisto Suominen 	vol[1] = (31 - (ucontrol->value.integer.value[1] & 31))
3547ae44cfaSRisto Suominen 		| (amp->amp_vol[index][1] & 32);
3551da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
3561da177e4SLinus Torvalds }
3571da177e4SLinus Torvalds 
snd_pmac_awacs_get_switch_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)35865b29f50STakashi Iwai static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol,
35965b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3601da177e4SLinus Torvalds {
36165b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3621da177e4SLinus Torvalds 	int index = kcontrol->private_value;
36365b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3645e246b85STakashi Iwai 
3657ae44cfaSRisto Suominen 	ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32)
3667ae44cfaSRisto Suominen 					? 0 : 1;
3677ae44cfaSRisto Suominen 	ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32)
3687ae44cfaSRisto Suominen 					? 0 : 1;
3691da177e4SLinus Torvalds 	return 0;
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
snd_pmac_awacs_put_switch_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)37265b29f50STakashi Iwai static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol,
37365b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3741da177e4SLinus Torvalds {
37565b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3761da177e4SLinus Torvalds 	int index = kcontrol->private_value;
3771da177e4SLinus Torvalds 	int vol[2];
37865b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3791da177e4SLinus Torvalds 
3807ae44cfaSRisto Suominen 	vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32)
3817ae44cfaSRisto Suominen 		| (amp->amp_vol[index][0] & 31);
3827ae44cfaSRisto Suominen 	vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32)
3837ae44cfaSRisto Suominen 		| (amp->amp_vol[index][1] & 31);
3841da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
3851da177e4SLinus Torvalds }
3861da177e4SLinus Torvalds 
snd_pmac_awacs_info_tone_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)38765b29f50STakashi Iwai static int snd_pmac_awacs_info_tone_amp(struct snd_kcontrol *kcontrol,
38865b29f50STakashi Iwai 					struct snd_ctl_elem_info *uinfo)
3891da177e4SLinus Torvalds {
3901da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3911da177e4SLinus Torvalds 	uinfo->count = 1;
3921da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3931da177e4SLinus Torvalds 	uinfo->value.integer.max = 14;
3941da177e4SLinus Torvalds 	return 0;
3951da177e4SLinus Torvalds }
3961da177e4SLinus Torvalds 
snd_pmac_awacs_get_tone_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)39765b29f50STakashi Iwai static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol,
39865b29f50STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
3991da177e4SLinus Torvalds {
40065b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
4011da177e4SLinus Torvalds 	int index = kcontrol->private_value;
40265b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
4035e246b85STakashi Iwai 
4041da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = amp->amp_tone[index];
4051da177e4SLinus Torvalds 	return 0;
4061da177e4SLinus Torvalds }
4071da177e4SLinus Torvalds 
snd_pmac_awacs_put_tone_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)40865b29f50STakashi Iwai static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol,
40965b29f50STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
4101da177e4SLinus Torvalds {
41165b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
4121da177e4SLinus Torvalds 	int index = kcontrol->private_value;
41365b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
414d4079ac4STakashi Iwai 	unsigned int val;
4155e246b85STakashi Iwai 
416d4079ac4STakashi Iwai 	val = ucontrol->value.integer.value[0];
417d4079ac4STakashi Iwai 	if (val > 14)
418d4079ac4STakashi Iwai 		return -EINVAL;
419d4079ac4STakashi Iwai 	if (val != amp->amp_tone[index]) {
420d4079ac4STakashi Iwai 		amp->amp_tone[index] = val;
4211da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
4221da177e4SLinus Torvalds 		return 1;
4231da177e4SLinus Torvalds 	}
4241da177e4SLinus Torvalds 	return 0;
4251da177e4SLinus Torvalds }
4261da177e4SLinus Torvalds 
snd_pmac_awacs_info_master_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)42765b29f50STakashi Iwai static int snd_pmac_awacs_info_master_amp(struct snd_kcontrol *kcontrol,
42865b29f50STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
4291da177e4SLinus Torvalds {
4301da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4311da177e4SLinus Torvalds 	uinfo->count = 1;
4321da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
4331da177e4SLinus Torvalds 	uinfo->value.integer.max = 99;
4341da177e4SLinus Torvalds 	return 0;
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
snd_pmac_awacs_get_master_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)43765b29f50STakashi Iwai static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol,
43865b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
4391da177e4SLinus Torvalds {
44065b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
44165b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
4425e246b85STakashi Iwai 
4431da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = amp->amp_master;
4441da177e4SLinus Torvalds 	return 0;
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
snd_pmac_awacs_put_master_amp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)44765b29f50STakashi Iwai static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
44865b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
4491da177e4SLinus Torvalds {
45065b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
45165b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
452d4079ac4STakashi Iwai 	unsigned int val;
4535e246b85STakashi Iwai 
454d4079ac4STakashi Iwai 	val = ucontrol->value.integer.value[0];
455d4079ac4STakashi Iwai 	if (val > 99)
456d4079ac4STakashi Iwai 		return -EINVAL;
457d4079ac4STakashi Iwai 	if (val != amp->amp_master) {
458d4079ac4STakashi Iwai 		amp->amp_master = val;
4591da177e4SLinus Torvalds 		awacs_amp_set_master(amp, amp->amp_master);
4601da177e4SLinus Torvalds 		return 1;
4611da177e4SLinus Torvalds 	}
4621da177e4SLinus Torvalds 	return 0;
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds #define AMP_CH_SPK	0
4661da177e4SLinus Torvalds #define AMP_CH_HD	1
4671da177e4SLinus Torvalds 
468c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] = {
4691da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
470ad1cd745SJaroslav Kysela 	  .name = "Speaker Playback Volume",
4711da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_volume_amp,
4721da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_volume_amp,
4731da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_volume_amp,
4741da177e4SLinus Torvalds 	  .private_value = AMP_CH_SPK,
4751da177e4SLinus Torvalds 	},
4761da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4771da177e4SLinus Torvalds 	  .name = "Headphone Playback Volume",
4781da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_volume_amp,
4791da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_volume_amp,
4801da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_volume_amp,
4811da177e4SLinus Torvalds 	  .private_value = AMP_CH_HD,
4821da177e4SLinus Torvalds 	},
4831da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4841da177e4SLinus Torvalds 	  .name = "Tone Control - Bass",
4851da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_tone_amp,
4861da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_tone_amp,
4871da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_tone_amp,
4881da177e4SLinus Torvalds 	  .private_value = 0,
4891da177e4SLinus Torvalds 	},
4901da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4911da177e4SLinus Torvalds 	  .name = "Tone Control - Treble",
4921da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_tone_amp,
4931da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_tone_amp,
4941da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_tone_amp,
4951da177e4SLinus Torvalds 	  .private_value = 1,
4961da177e4SLinus Torvalds 	},
4971da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4981da177e4SLinus Torvalds 	  .name = "Amp Master Playback Volume",
4991da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_master_amp,
5001da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_master_amp,
5011da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_master_amp,
5021da177e4SLinus Torvalds 	},
5031da177e4SLinus Torvalds };
5041da177e4SLinus Torvalds 
505905e46acSBhumika Goyal static const struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = {
5061da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5071da177e4SLinus Torvalds 	.name = "Headphone Playback Switch",
5081da177e4SLinus Torvalds 	.info = snd_pmac_boolean_stereo_info,
5091da177e4SLinus Torvalds 	.get = snd_pmac_awacs_get_switch_amp,
5101da177e4SLinus Torvalds 	.put = snd_pmac_awacs_put_switch_amp,
5111da177e4SLinus Torvalds 	.private_value = AMP_CH_HD,
5121da177e4SLinus Torvalds };
5131da177e4SLinus Torvalds 
514905e46acSBhumika Goyal static const struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw = {
5151da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
516ad1cd745SJaroslav Kysela 	.name = "Speaker Playback Switch",
5171da177e4SLinus Torvalds 	.info = snd_pmac_boolean_stereo_info,
5181da177e4SLinus Torvalds 	.get = snd_pmac_awacs_get_switch_amp,
5191da177e4SLinus Torvalds 	.put = snd_pmac_awacs_put_switch_amp,
5201da177e4SLinus Torvalds 	.private_value = AMP_CH_SPK,
5211da177e4SLinus Torvalds };
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds /*
5271da177e4SLinus Torvalds  * mic boost for screamer
5281da177e4SLinus Torvalds  */
snd_pmac_screamer_mic_boost_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)52965b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol,
53065b29f50STakashi Iwai 					    struct snd_ctl_elem_info *uinfo)
5311da177e4SLinus Torvalds {
5321da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
5331da177e4SLinus Torvalds 	uinfo->count = 1;
5341da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
535a8c2a6bfSRisto Suominen 	uinfo->value.integer.max = 3;
5361da177e4SLinus Torvalds 	return 0;
5371da177e4SLinus Torvalds }
5381da177e4SLinus Torvalds 
snd_pmac_screamer_mic_boost_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)53965b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol,
54065b29f50STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
5411da177e4SLinus Torvalds {
54265b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
543a8c2a6bfSRisto Suominen 	int val = 0;
5441da177e4SLinus Torvalds 	unsigned long flags;
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
5471da177e4SLinus Torvalds 	if (chip->awacs_reg[6] & MASK_MIC_BOOST)
548a8c2a6bfSRisto Suominen 		val |= 2;
549a8c2a6bfSRisto Suominen 	if (chip->awacs_reg[0] & MASK_GAINLINE)
550a8c2a6bfSRisto Suominen 		val |= 1;
5511da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
5521da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
5531da177e4SLinus Torvalds 	return 0;
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds 
snd_pmac_screamer_mic_boost_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)55665b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
55765b29f50STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
5581da177e4SLinus Torvalds {
55965b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
5601da177e4SLinus Torvalds 	int changed = 0;
5611da177e4SLinus Torvalds 	int val0, val6;
5621da177e4SLinus Torvalds 	unsigned long flags;
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
5651da177e4SLinus Torvalds 	val0 = chip->awacs_reg[0] & ~MASK_GAINLINE;
5661da177e4SLinus Torvalds 	val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST;
567a8c2a6bfSRisto Suominen 	if (ucontrol->value.integer.value[0] & 1)
5681da177e4SLinus Torvalds 		val0 |= MASK_GAINLINE;
569a8c2a6bfSRisto Suominen 	if (ucontrol->value.integer.value[0] & 2)
5701da177e4SLinus Torvalds 		val6 |= MASK_MIC_BOOST;
5711da177e4SLinus Torvalds 	if (val0 != chip->awacs_reg[0]) {
5721da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 0, val0);
5731da177e4SLinus Torvalds 		changed = 1;
5741da177e4SLinus Torvalds 	}
5751da177e4SLinus Torvalds 	if (val6 != chip->awacs_reg[6]) {
5761da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 6, val6);
5771da177e4SLinus Torvalds 		changed = 1;
5781da177e4SLinus Torvalds 	}
5791da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
5801da177e4SLinus Torvalds 	return changed;
5811da177e4SLinus Torvalds }
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds /*
5841da177e4SLinus Torvalds  * lists of mixer elements
5851da177e4SLinus Torvalds  */
586c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers[] = {
5871da177e4SLinus Torvalds 	AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
588a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Capture Volume", 0, 4, 0),
589a8c2a6bfSRisto Suominen /*	AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */
590a8c2a6bfSRisto Suominen };
591a8c2a6bfSRisto Suominen 
592c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] = {
593a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
594a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
595a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
596a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
597a8c2a6bfSRisto Suominen };
598a8c2a6bfSRisto Suominen 
599c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] = {
600a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
601dca7c741SRisto Suominen };
602dca7c741SRisto Suominen 
603c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] = {
604dca7c741SRisto Suominen 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
605a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
606a8c2a6bfSRisto Suominen };
607a8c2a6bfSRisto Suominen 
608c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] = {
6094dbf95baSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
6104dbf95baSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
6114dbf95baSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
6124dbf95baSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
6134dbf95baSRisto Suominen };
6144dbf95baSRisto Suominen 
615c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] = {
616a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
617a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
618a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
619a8c2a6bfSRisto Suominen };
620a8c2a6bfSRisto Suominen 
621c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] = {
622dca7c741SRisto Suominen 	AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1),
623dca7c741SRisto Suominen };
624dca7c741SRisto Suominen 
625c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] = {
626a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
6271da177e4SLinus Torvalds 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
6281da177e4SLinus Torvalds };
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds /* FIXME: is this correct order?
6311da177e4SLinus Torvalds  * screamer (powerbook G3 pismo) seems to have different bits...
6321da177e4SLinus Torvalds  */
633c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers2[] = {
6341da177e4SLinus Torvalds 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
6351da177e4SLinus Torvalds 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
6361da177e4SLinus Torvalds };
6371da177e4SLinus Torvalds 
638c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers2[] = {
6391da177e4SLinus Torvalds 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
6401da177e4SLinus Torvalds 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
6411da177e4SLinus Torvalds };
6421da177e4SLinus Torvalds 
643c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] = {
644dca7c741SRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
645dca7c741SRisto Suominen };
646dca7c741SRisto Suominen 
647c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_master_sw =
6481da177e4SLinus Torvalds AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
6491da177e4SLinus Torvalds 
650c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac =
651a8c2a6bfSRisto Suominen AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
652a8c2a6bfSRisto Suominen 
653c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 =
654dca7c741SRisto Suominen AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1);
655dca7c741SRisto Suominen 
656c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] = {
657a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
6581da177e4SLinus Torvalds };
6591da177e4SLinus Torvalds 
660c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] = {
6611da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
662a8c2a6bfSRisto Suominen 	  .name = "Mic Boost Capture Volume",
6631da177e4SLinus Torvalds 	  .info = snd_pmac_screamer_mic_boost_info,
6641da177e4SLinus Torvalds 	  .get = snd_pmac_screamer_mic_boost_get,
6651da177e4SLinus Torvalds 	  .put = snd_pmac_screamer_mic_boost_put,
6661da177e4SLinus Torvalds 	},
6671da177e4SLinus Torvalds };
6681da177e4SLinus Torvalds 
669c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] =
670a8c2a6bfSRisto Suominen {
671a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
672a8c2a6bfSRisto Suominen };
673a8c2a6bfSRisto Suominen 
674c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] =
675a8c2a6bfSRisto Suominen {
676a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
677a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
678a8c2a6bfSRisto Suominen };
679a8c2a6bfSRisto Suominen 
680c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] =
681a8c2a6bfSRisto Suominen {
682a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
683a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
684a8c2a6bfSRisto Suominen };
685a8c2a6bfSRisto Suominen 
686c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] = {
687ad1cd745SJaroslav Kysela 	AWACS_VOLUME("Speaker Playback Volume", 4, 6, 1),
6881da177e4SLinus Torvalds };
689a8c2a6bfSRisto Suominen 
690c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw =
691ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
6921da177e4SLinus Torvalds 
693c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 =
694ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
695030b655bSRisto Suominen 
696c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 =
697ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
698a8c2a6bfSRisto Suominen 
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds /*
7011da177e4SLinus Torvalds  * add new mixer elements to the card
7021da177e4SLinus Torvalds  */
build_mixers(struct snd_pmac * chip,int nums,const struct snd_kcontrol_new * mixers)7037ae44cfaSRisto Suominen static int build_mixers(struct snd_pmac *chip, int nums,
704c031b0ccSTakashi Iwai 			const struct snd_kcontrol_new *mixers)
7051da177e4SLinus Torvalds {
7061da177e4SLinus Torvalds 	int i, err;
7071da177e4SLinus Torvalds 
7081da177e4SLinus Torvalds 	for (i = 0; i < nums; i++) {
7097ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip));
7107ae44cfaSRisto Suominen 		if (err < 0)
7111da177e4SLinus Torvalds 			return err;
7121da177e4SLinus Torvalds 	}
7131da177e4SLinus Torvalds 	return 0;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds /*
7181da177e4SLinus Torvalds  * restore all registers
7191da177e4SLinus Torvalds  */
awacs_restore_all_regs(struct snd_pmac * chip)72065b29f50STakashi Iwai static void awacs_restore_all_regs(struct snd_pmac *chip)
7211da177e4SLinus Torvalds {
7221da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
7231da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
7241da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]);
7251da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]);
7261da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
7271da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]);
7281da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
7291da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]);
7301da177e4SLinus Torvalds 	}
7311da177e4SLinus Torvalds }
7321da177e4SLinus Torvalds 
7338c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
snd_pmac_awacs_suspend(struct snd_pmac * chip)73465b29f50STakashi Iwai static void snd_pmac_awacs_suspend(struct snd_pmac *chip)
7351da177e4SLinus Torvalds {
7361da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1]
7371da177e4SLinus Torvalds 					     | MASK_AMUTE | MASK_CMUTE));
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds 
snd_pmac_awacs_resume(struct snd_pmac * chip)74065b29f50STakashi Iwai static void snd_pmac_awacs_resume(struct snd_pmac *chip)
7411da177e4SLinus Torvalds {
74271a157e8SGrant Likely 	if (of_machine_is_compatible("PowerBook3,1")
74371a157e8SGrant Likely 	    || of_machine_is_compatible("PowerBook3,2")) {
744989a0b24SNishanth Aravamudan 		msleep(100);
7451da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 1,
7461da177e4SLinus Torvalds 			chip->awacs_reg[1] & ~MASK_PAROUT);
747989a0b24SNishanth Aravamudan 		msleep(300);
7481da177e4SLinus Torvalds 	}
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds 	awacs_restore_all_regs(chip);
7511da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
7521da177e4SLinus Torvalds 		/* reset power bits in reg 6 */
7531da177e4SLinus Torvalds 		mdelay(5);
7541da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
7551da177e4SLinus Torvalds 	}
7561da177e4SLinus Torvalds 	screamer_recalibrate(chip);
7571da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
7581da177e4SLinus Torvalds 	if (chip->mixer_data) {
75965b29f50STakashi Iwai 		struct awacs_amp *amp = chip->mixer_data;
7607ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 0,
7617ae44cfaSRisto Suominen 				  amp->amp_vol[0][0], amp->amp_vol[0][1], 0);
7627ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 1,
7637ae44cfaSRisto Suominen 				  amp->amp_vol[1][0], amp->amp_vol[1][1], 0);
7641da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
7651da177e4SLinus Torvalds 		awacs_amp_set_master(amp, amp->amp_master);
7661da177e4SLinus Torvalds 	}
7671da177e4SLinus Torvalds #endif
7681da177e4SLinus Torvalds }
7698c870933SBenjamin Herrenschmidt #endif /* CONFIG_PM */
7701da177e4SLinus Torvalds 
77171a157e8SGrant Likely #define IS_PM7500 (of_machine_is_compatible("AAPL,7500") \
77271a157e8SGrant Likely 		|| of_machine_is_compatible("AAPL,8500") \
77371a157e8SGrant Likely 		|| of_machine_is_compatible("AAPL,9500"))
77471a157e8SGrant Likely #define IS_PM5500 (of_machine_is_compatible("AAPL,e411"))
77571a157e8SGrant Likely #define IS_BEIGE (of_machine_is_compatible("AAPL,Gossamer"))
77671a157e8SGrant Likely #define IS_IMAC1 (of_machine_is_compatible("PowerMac2,1"))
77771a157e8SGrant Likely #define IS_IMAC2 (of_machine_is_compatible("PowerMac2,2") \
77871a157e8SGrant Likely 		|| of_machine_is_compatible("PowerMac4,1"))
77971a157e8SGrant Likely #define IS_G4AGP (of_machine_is_compatible("PowerMac3,1"))
78071a157e8SGrant Likely #define IS_LOMBARD (of_machine_is_compatible("PowerBook1,1"))
781a8c2a6bfSRisto Suominen 
782030b655bSRisto Suominen static int imac1, imac2;
783a8c2a6bfSRisto Suominen 
7841da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE
7851da177e4SLinus Torvalds /*
7861da177e4SLinus Torvalds  * auto-mute stuffs
7871da177e4SLinus Torvalds  */
snd_pmac_awacs_detect_headphone(struct snd_pmac * chip)78865b29f50STakashi Iwai static int snd_pmac_awacs_detect_headphone(struct snd_pmac *chip)
7891da177e4SLinus Torvalds {
7901da177e4SLinus Torvalds 	return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0;
7911da177e4SLinus Torvalds }
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
toggle_amp_mute(struct awacs_amp * amp,int index,int mute)79465b29f50STakashi Iwai static int toggle_amp_mute(struct awacs_amp *amp, int index, int mute)
7951da177e4SLinus Torvalds {
7961da177e4SLinus Torvalds 	int vol[2];
7971da177e4SLinus Torvalds 	vol[0] = amp->amp_vol[index][0] & 31;
7981da177e4SLinus Torvalds 	vol[1] = amp->amp_vol[index][1] & 31;
7991da177e4SLinus Torvalds 	if (mute) {
8001da177e4SLinus Torvalds 		vol[0] |= 32;
8011da177e4SLinus Torvalds 		vol[1] |= 32;
8021da177e4SLinus Torvalds 	}
8031da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
8041da177e4SLinus Torvalds }
8051da177e4SLinus Torvalds #endif
8061da177e4SLinus Torvalds 
snd_pmac_awacs_update_automute(struct snd_pmac * chip,int do_notify)80765b29f50STakashi Iwai static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)
8081da177e4SLinus Torvalds {
8091da177e4SLinus Torvalds 	if (chip->auto_mute) {
8101da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
8111da177e4SLinus Torvalds 		if (chip->mixer_data) {
81265b29f50STakashi Iwai 			struct awacs_amp *amp = chip->mixer_data;
8131da177e4SLinus Torvalds 			int changed;
8141da177e4SLinus Torvalds 			if (snd_pmac_awacs_detect_headphone(chip)) {
8151da177e4SLinus Torvalds 				changed = toggle_amp_mute(amp, AMP_CH_HD, 0);
8161da177e4SLinus Torvalds 				changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1);
8171da177e4SLinus Torvalds 			} else {
8181da177e4SLinus Torvalds 				changed = toggle_amp_mute(amp, AMP_CH_HD, 1);
8191da177e4SLinus Torvalds 				changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0);
8201da177e4SLinus Torvalds 			}
8211da177e4SLinus Torvalds 			if (do_notify && ! changed)
8221da177e4SLinus Torvalds 				return;
8231da177e4SLinus Torvalds 		} else
8241da177e4SLinus Torvalds #endif
8251da177e4SLinus Torvalds 		{
826a8c2a6bfSRisto Suominen 			int reg = chip->awacs_reg[1]
827a8c2a6bfSRisto Suominen 				| (MASK_HDMUTE | MASK_SPKMUTE);
828030b655bSRisto Suominen 			if (imac1) {
829030b655bSRisto Suominen 				reg &= ~MASK_SPKMUTE;
830030b655bSRisto Suominen 				reg |= MASK_PAROUT1;
831030b655bSRisto Suominen 			} else if (imac2) {
832a8c2a6bfSRisto Suominen 				reg &= ~MASK_SPKMUTE;
833a8c2a6bfSRisto Suominen 				reg &= ~MASK_PAROUT1;
834a8c2a6bfSRisto Suominen 			}
8351da177e4SLinus Torvalds 			if (snd_pmac_awacs_detect_headphone(chip))
8361da177e4SLinus Torvalds 				reg &= ~MASK_HDMUTE;
837030b655bSRisto Suominen 			else if (imac1)
838030b655bSRisto Suominen 				reg &= ~MASK_PAROUT1;
839030b655bSRisto Suominen 			else if (imac2)
840a8c2a6bfSRisto Suominen 				reg |= MASK_PAROUT1;
8411da177e4SLinus Torvalds 			else
8421da177e4SLinus Torvalds 				reg &= ~MASK_SPKMUTE;
8431da177e4SLinus Torvalds 			if (do_notify && reg == chip->awacs_reg[1])
8441da177e4SLinus Torvalds 				return;
8451da177e4SLinus Torvalds 			snd_pmac_awacs_write_reg(chip, 1, reg);
8461da177e4SLinus Torvalds 		}
8471da177e4SLinus Torvalds 		if (do_notify) {
8481da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8491da177e4SLinus Torvalds 				       &chip->master_sw_ctl->id);
8501da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8511da177e4SLinus Torvalds 				       &chip->speaker_sw_ctl->id);
8521da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8531da177e4SLinus Torvalds 				       &chip->hp_detect_ctl->id);
8541da177e4SLinus Torvalds 		}
8551da177e4SLinus Torvalds 	}
8561da177e4SLinus Torvalds }
8571da177e4SLinus Torvalds #endif /* PMAC_SUPPORT_AUTOMUTE */
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds /*
8611da177e4SLinus Torvalds  * initialize chip
8621da177e4SLinus Torvalds  */
86315afafc2SBill Pemberton int
snd_pmac_awacs_init(struct snd_pmac * chip)86465b29f50STakashi Iwai snd_pmac_awacs_init(struct snd_pmac *chip)
8651da177e4SLinus Torvalds {
866a8c2a6bfSRisto Suominen 	int pm7500 = IS_PM7500;
867b0a8a8fdSRisto Suominen 	int pm5500 = IS_PM5500;
868a8c2a6bfSRisto Suominen 	int beige = IS_BEIGE;
8694dbf95baSRisto Suominen 	int g4agp = IS_G4AGP;
870573934bcSRisto Suominen 	int lombard = IS_LOMBARD;
871030b655bSRisto Suominen 	int imac;
8721da177e4SLinus Torvalds 	int err, vol;
873dca7c741SRisto Suominen 	struct snd_kcontrol *vmaster_sw, *vmaster_vol;
874dca7c741SRisto Suominen 	struct snd_kcontrol *master_vol, *speaker_vol;
8751da177e4SLinus Torvalds 
876030b655bSRisto Suominen 	imac1 = IS_IMAC1;
877030b655bSRisto Suominen 	imac2 = IS_IMAC2;
878030b655bSRisto Suominen 	imac = imac1 || imac2;
8791da177e4SLinus Torvalds 	/* looks like MASK_GAINLINE triggers something, so we set here
8801da177e4SLinus Torvalds 	 * as start-up
8811da177e4SLinus Torvalds 	 */
8821da177e4SLinus Torvalds 	chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE;
8831da177e4SLinus Torvalds 	chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE;
8841da177e4SLinus Torvalds 	/* FIXME: Only machines with external SRS module need MASK_PAROUT */
8851da177e4SLinus Torvalds 	if (chip->has_iic || chip->device_id == 0x5 ||
8861da177e4SLinus Torvalds 	    /* chip->_device_id == 0x8 || */
8871da177e4SLinus Torvalds 	    chip->device_id == 0xb)
8881da177e4SLinus Torvalds 		chip->awacs_reg[1] |= MASK_PAROUT;
8891da177e4SLinus Torvalds 	/* get default volume from nvram */
8901da177e4SLinus Torvalds 	// vol = (~nvram_read_byte(0x1308) & 7) << 1;
8911da177e4SLinus Torvalds 	// vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
8921da177e4SLinus Torvalds 	vol = 0x0f; /* no, on alsa, muted as default */
8931da177e4SLinus Torvalds 	vol = vol + (vol << 6);
8941da177e4SLinus Torvalds 	chip->awacs_reg[2] = vol;
8951da177e4SLinus Torvalds 	chip->awacs_reg[4] = vol;
8961da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
8977ae44cfaSRisto Suominen 		/* FIXME: screamer has loopthru vol control */
8987ae44cfaSRisto Suominen 		chip->awacs_reg[5] = vol;
8997ae44cfaSRisto Suominen 		/* FIXME: maybe should be vol << 3 for PCMCIA speaker */
9007ae44cfaSRisto Suominen 		chip->awacs_reg[6] = MASK_MIC_BOOST;
9011da177e4SLinus Torvalds 		chip->awacs_reg[7] = 0;
9021da177e4SLinus Torvalds 	}
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds 	awacs_restore_all_regs(chip);
9051da177e4SLinus Torvalds 	chip->manufacturer = (in_le32(&chip->awacs->codec_stat) >> 8) & 0xf;
9061da177e4SLinus Torvalds 	screamer_recalibrate(chip);
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds 	chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;
9091da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
9101da177e4SLinus Torvalds 	if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) {
91159feddb2SPanagiotis Issaris 		struct awacs_amp *amp = kzalloc(sizeof(*amp), GFP_KERNEL);
9121da177e4SLinus Torvalds 		if (! amp)
9131da177e4SLinus Torvalds 			return -ENOMEM;
9141da177e4SLinus Torvalds 		chip->mixer_data = amp;
9151da177e4SLinus Torvalds 		chip->mixer_free = awacs_amp_free;
9167ae44cfaSRisto Suominen 		/* mute and zero vol */
9177ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 0, 63, 63, 0);
9181da177e4SLinus Torvalds 		awacs_amp_set_vol(amp, 1, 63, 63, 0);
9191da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, 7, 7); /* 0 dB */
9201da177e4SLinus Torvalds 		awacs_amp_set_master(amp, 79); /* 0 dB */
9211da177e4SLinus Torvalds 	}
9221da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds 	if (chip->hp_stat_mask == 0) {
9251da177e4SLinus Torvalds 		/* set headphone-jack detection bit */
9261da177e4SLinus Torvalds 		switch (chip->model) {
9271da177e4SLinus Torvalds 		case PMAC_AWACS:
928b0a8a8fdSRisto Suominen 			chip->hp_stat_mask = pm7500 || pm5500 ? MASK_HDPCONN
929a8c2a6bfSRisto Suominen 				: MASK_LOCONN;
9301da177e4SLinus Torvalds 			break;
9311da177e4SLinus Torvalds 		case PMAC_SCREAMER:
9321da177e4SLinus Torvalds 			switch (chip->device_id) {
9331da177e4SLinus Torvalds 			case 0x08:
934a8c2a6bfSRisto Suominen 			case 0x0B:
935a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = imac
936a8c2a6bfSRisto Suominen 					? MASK_LOCONN_IMAC |
937a8c2a6bfSRisto Suominen 					MASK_HDPLCONN_IMAC |
938a8c2a6bfSRisto Suominen 					MASK_HDPRCONN_IMAC
939a8c2a6bfSRisto Suominen 					: MASK_HDPCONN;
9401da177e4SLinus Torvalds 				break;
9411da177e4SLinus Torvalds 			case 0x00:
9421da177e4SLinus Torvalds 			case 0x05:
943a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = MASK_LOCONN;
9441da177e4SLinus Torvalds 				break;
9451da177e4SLinus Torvalds 			default:
946a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = MASK_HDPCONN;
9471da177e4SLinus Torvalds 				break;
9481da177e4SLinus Torvalds 			}
9491da177e4SLinus Torvalds 			break;
9501da177e4SLinus Torvalds 		default:
9511da177e4SLinus Torvalds 			snd_BUG();
9521da177e4SLinus Torvalds 			break;
9531da177e4SLinus Torvalds 		}
9541da177e4SLinus Torvalds 	}
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds 	/*
9571da177e4SLinus Torvalds 	 * build mixers
9581da177e4SLinus Torvalds 	 */
9591da177e4SLinus Torvalds 	strcpy(chip->card->mixername, "PowerMac AWACS");
9601da177e4SLinus Torvalds 
9617ae44cfaSRisto Suominen 	err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers),
9627ae44cfaSRisto Suominen 				snd_pmac_awacs_mixers);
9637ae44cfaSRisto Suominen 	if (err < 0)
9641da177e4SLinus Torvalds 		return err;
9654dbf95baSRisto Suominen 	if (beige || g4agp)
966a8c2a6bfSRisto Suominen 		;
967b0a8a8fdSRisto Suominen 	else if (chip->model == PMAC_SCREAMER || pm5500)
9681da177e4SLinus Torvalds 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2),
9691da177e4SLinus Torvalds 				   snd_pmac_screamer_mixers2);
970a8c2a6bfSRisto Suominen 	else if (!pm7500)
9711da177e4SLinus Torvalds 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2),
9721da177e4SLinus Torvalds 				   snd_pmac_awacs_mixers2);
9731da177e4SLinus Torvalds 	if (err < 0)
9741da177e4SLinus Torvalds 		return err;
975dca7c741SRisto Suominen 	if (pm5500) {
976dca7c741SRisto Suominen 		err = build_mixers(chip,
977dca7c741SRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers2_pmac5500),
978dca7c741SRisto Suominen 				   snd_pmac_awacs_mixers2_pmac5500);
979dca7c741SRisto Suominen 		if (err < 0)
980dca7c741SRisto Suominen 			return err;
981dca7c741SRisto Suominen 	}
982b268c34eSArnd Bergmann 	master_vol = NULL;
983a8c2a6bfSRisto Suominen 	if (pm7500)
984a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
985a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
986a8c2a6bfSRisto Suominen 				   snd_pmac_awacs_mixers_pmac7500);
987dca7c741SRisto Suominen 	else if (pm5500)
988dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
989dca7c741SRisto Suominen 		    (master_vol = snd_ctl_new1(snd_pmac_awacs_mixers_pmac5500,
990dca7c741SRisto Suominen 						chip)));
991a8c2a6bfSRisto Suominen 	else if (beige)
992a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
993a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_beige),
994a8c2a6bfSRisto Suominen 				   snd_pmac_screamer_mixers_beige);
995dca7c741SRisto Suominen 	else if (imac || lombard) {
996dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
997dca7c741SRisto Suominen 		    (master_vol = snd_ctl_new1(snd_pmac_screamer_mixers_lo,
998dca7c741SRisto Suominen 						chip)));
999dca7c741SRisto Suominen 		if (err < 0)
1000dca7c741SRisto Suominen 			return err;
1001a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1002a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_imac),
1003a8c2a6bfSRisto Suominen 				   snd_pmac_screamer_mixers_imac);
1004dca7c741SRisto Suominen 	} else if (g4agp)
10054dbf95baSRisto Suominen 		err = build_mixers(chip,
10064dbf95baSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_g4agp),
10074dbf95baSRisto Suominen 				   snd_pmac_screamer_mixers_g4agp);
1008a8c2a6bfSRisto Suominen 	else
1009a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1010a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac),
1011a8c2a6bfSRisto Suominen 				   snd_pmac_awacs_mixers_pmac);
1012a8c2a6bfSRisto Suominen 	if (err < 0)
1013a8c2a6bfSRisto Suominen 		return err;
1014573934bcSRisto Suominen 	chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp || lombard)
1015a8c2a6bfSRisto Suominen 			? &snd_pmac_awacs_master_sw_imac
1016dca7c741SRisto Suominen 			: pm5500
1017dca7c741SRisto Suominen 			? &snd_pmac_awacs_master_sw_pmac5500
1018a8c2a6bfSRisto Suominen 			: &snd_pmac_awacs_master_sw, chip);
10197ae44cfaSRisto Suominen 	err = snd_ctl_add(chip->card, chip->master_sw_ctl);
10207ae44cfaSRisto Suominen 	if (err < 0)
10211da177e4SLinus Torvalds 		return err;
10221da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
10231da177e4SLinus Torvalds 	if (chip->mixer_data) {
10241da177e4SLinus Torvalds 		/* use amplifier.  the signal is connected from route A
10251da177e4SLinus Torvalds 		 * to the amp.  the amp has its headphone and speaker
10261da177e4SLinus Torvalds 		 * volumes and mute switches, so we use them instead of
10271da177e4SLinus Torvalds 		 * screamer registers.
10281da177e4SLinus Torvalds 		 * in this case, it seems the route C is not used.
10291da177e4SLinus Torvalds 		 */
10307ae44cfaSRisto Suominen 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol),
10317ae44cfaSRisto Suominen 					snd_pmac_awacs_amp_vol);
10327ae44cfaSRisto Suominen 		if (err < 0)
10331da177e4SLinus Torvalds 			return err;
10341da177e4SLinus Torvalds 		/* overwrite */
10357ae44cfaSRisto Suominen 		chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw,
10367ae44cfaSRisto Suominen 							chip);
10377ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->master_sw_ctl);
10387ae44cfaSRisto Suominen 		if (err < 0)
10391da177e4SLinus Torvalds 			return err;
10407ae44cfaSRisto Suominen 		chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw,
10417ae44cfaSRisto Suominen 							chip);
10427ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
10437ae44cfaSRisto Suominen 		if (err < 0)
10441da177e4SLinus Torvalds 			return err;
10451da177e4SLinus Torvalds 	} else
10461da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
10471da177e4SLinus Torvalds 	{
10481da177e4SLinus Torvalds 		/* route A = headphone, route C = speaker */
1049dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
1050dca7c741SRisto Suominen 		    (speaker_vol = snd_ctl_new1(snd_pmac_awacs_speaker_vol,
1051dca7c741SRisto Suominen 						chip)));
10527ae44cfaSRisto Suominen 		if (err < 0)
10531da177e4SLinus Torvalds 			return err;
1054030b655bSRisto Suominen 		chip->speaker_sw_ctl = snd_ctl_new1(imac1
1055030b655bSRisto Suominen 				? &snd_pmac_awacs_speaker_sw_imac1
1056030b655bSRisto Suominen 				: imac2
1057030b655bSRisto Suominen 				? &snd_pmac_awacs_speaker_sw_imac2
1058a8c2a6bfSRisto Suominen 				: &snd_pmac_awacs_speaker_sw, chip);
10597ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
10607ae44cfaSRisto Suominen 		if (err < 0)
10611da177e4SLinus Torvalds 			return err;
10621da177e4SLinus Torvalds 	}
10631da177e4SLinus Torvalds 
1064dca7c741SRisto Suominen 	if (pm5500 || imac || lombard) {
1065dca7c741SRisto Suominen 		vmaster_sw = snd_ctl_make_virtual_master(
1066dca7c741SRisto Suominen 			"Master Playback Switch", (unsigned int *) NULL);
10679ab0cb30STakashi Iwai 		err = snd_ctl_add_follower_uncached(vmaster_sw,
1068dca7c741SRisto Suominen 						    chip->master_sw_ctl);
1069dca7c741SRisto Suominen 		if (err < 0)
1070dca7c741SRisto Suominen 			return err;
10719ab0cb30STakashi Iwai 		err = snd_ctl_add_follower_uncached(vmaster_sw,
1072dca7c741SRisto Suominen 						    chip->speaker_sw_ctl);
1073dca7c741SRisto Suominen 		if (err < 0)
1074dca7c741SRisto Suominen 			return err;
1075dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card, vmaster_sw);
1076dca7c741SRisto Suominen 		if (err < 0)
1077dca7c741SRisto Suominen 			return err;
1078dca7c741SRisto Suominen 		vmaster_vol = snd_ctl_make_virtual_master(
1079dca7c741SRisto Suominen 			"Master Playback Volume", (unsigned int *) NULL);
10809ab0cb30STakashi Iwai 		err = snd_ctl_add_follower(vmaster_vol, master_vol);
1081dca7c741SRisto Suominen 		if (err < 0)
1082dca7c741SRisto Suominen 			return err;
10839ab0cb30STakashi Iwai 		err = snd_ctl_add_follower(vmaster_vol, speaker_vol);
1084dca7c741SRisto Suominen 		if (err < 0)
1085dca7c741SRisto Suominen 			return err;
1086dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card, vmaster_vol);
1087dca7c741SRisto Suominen 		if (err < 0)
1088dca7c741SRisto Suominen 			return err;
1089dca7c741SRisto Suominen 	}
1090dca7c741SRisto Suominen 
10914dbf95baSRisto Suominen 	if (beige || g4agp)
1092a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1093a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige),
1094a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost_beige);
1095a8c2a6bfSRisto Suominen 	else if (imac)
1096a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1097a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost_imac),
1098a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost_imac);
1099a8c2a6bfSRisto Suominen 	else if (chip->model == PMAC_SCREAMER)
1100a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1101a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost),
1102a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost);
1103a8c2a6bfSRisto Suominen 	else if (pm7500)
1104a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1105a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_awacs_mic_boost_pmac7500),
1106a8c2a6bfSRisto Suominen 				snd_pmac_awacs_mic_boost_pmac7500);
1107a8c2a6bfSRisto Suominen 	else
1108a8c2a6bfSRisto Suominen 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost),
1109a8c2a6bfSRisto Suominen 				snd_pmac_awacs_mic_boost);
1110a8c2a6bfSRisto Suominen 	if (err < 0)
11111da177e4SLinus Torvalds 		return err;
11121da177e4SLinus Torvalds 
11131da177e4SLinus Torvalds 	/*
11141da177e4SLinus Torvalds 	 * set lowlevel callbacks
11151da177e4SLinus Torvalds 	 */
11161da177e4SLinus Torvalds 	chip->set_format = snd_pmac_awacs_set_format;
11178c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
11181da177e4SLinus Torvalds 	chip->suspend = snd_pmac_awacs_suspend;
11191da177e4SLinus Torvalds 	chip->resume = snd_pmac_awacs_resume;
11201da177e4SLinus Torvalds #endif
11211da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE
11227ae44cfaSRisto Suominen 	err = snd_pmac_add_automute(chip);
11237ae44cfaSRisto Suominen 	if (err < 0)
11241da177e4SLinus Torvalds 		return err;
11251da177e4SLinus Torvalds 	chip->detect_headphone = snd_pmac_awacs_detect_headphone;
11261da177e4SLinus Torvalds 	chip->update_automute = snd_pmac_awacs_update_automute;
11271da177e4SLinus Torvalds 	snd_pmac_awacs_update_automute(chip, 0); /* update the status only */
11281da177e4SLinus Torvalds #endif
11291da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
11301da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
11311da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
11321da177e4SLinus Torvalds 	}
11331da177e4SLinus Torvalds 
11341da177e4SLinus Torvalds 	return 0;
11351da177e4SLinus Torvalds }
1136