xref: /openbmc/linux/sound/ppc/awacs.c (revision c031b0cc)
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>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <sound/core.h>
161da177e4SLinus Torvalds #include "pmac.h"
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #ifdef CONFIG_ADB_CUDA
201da177e4SLinus Torvalds #define PMAC_AMP_AVAIL
211da177e4SLinus Torvalds #endif
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
2465b29f50STakashi Iwai struct awacs_amp {
251da177e4SLinus Torvalds 	unsigned char amp_master;
261da177e4SLinus Torvalds 	unsigned char amp_vol[2][2];
271da177e4SLinus Torvalds 	unsigned char amp_tone[2];
2865b29f50STakashi Iwai };
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA)
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds 
3565b29f50STakashi Iwai static void snd_pmac_screamer_wait(struct snd_pmac *chip)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds 	long timeout = 2000;
381da177e4SLinus Torvalds 	while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) {
391da177e4SLinus Torvalds 		mdelay(1);
401da177e4SLinus Torvalds 		if (! --timeout) {
411da177e4SLinus Torvalds 			snd_printd("snd_pmac_screamer_wait timeout\n");
421da177e4SLinus Torvalds 			break;
431da177e4SLinus Torvalds 		}
441da177e4SLinus Torvalds 	}
451da177e4SLinus Torvalds }
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds /*
481da177e4SLinus Torvalds  * write AWACS register
491da177e4SLinus Torvalds  */
501da177e4SLinus Torvalds static void
5165b29f50STakashi Iwai snd_pmac_awacs_write(struct snd_pmac *chip, int val)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds 	long timeout = 5000000;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER)
561da177e4SLinus Torvalds 		snd_pmac_screamer_wait(chip);
571da177e4SLinus Torvalds 	out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22));
581da177e4SLinus Torvalds 	while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) {
591da177e4SLinus Torvalds 		if (! --timeout) {
601da177e4SLinus Torvalds 			snd_printd("snd_pmac_awacs_write timeout\n");
611da177e4SLinus Torvalds 			break;
621da177e4SLinus Torvalds 		}
631da177e4SLinus Torvalds 	}
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds static void
6765b29f50STakashi Iwai snd_pmac_awacs_write_reg(struct snd_pmac *chip, int reg, int val)
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds 	snd_pmac_awacs_write(chip, val | (reg << 12));
701da177e4SLinus Torvalds 	chip->awacs_reg[reg] = val;
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds static void
7465b29f50STakashi Iwai snd_pmac_awacs_write_noreg(struct snd_pmac *chip, int reg, int val)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds 	snd_pmac_awacs_write(chip, val | (reg << 12));
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
798c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
801da177e4SLinus Torvalds /* Recalibrate chip */
8165b29f50STakashi Iwai static void screamer_recalibrate(struct snd_pmac *chip)
821da177e4SLinus Torvalds {
831da177e4SLinus Torvalds 	if (chip->model != PMAC_SCREAMER)
841da177e4SLinus Torvalds 		return;
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	/* Sorry for the horrible delays... I hope to get that improved
871da177e4SLinus Torvalds 	 * by making the whole PM process asynchronous in a future version
881da177e4SLinus Torvalds 	 */
891da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
901da177e4SLinus Torvalds 	if (chip->manufacturer == 0x1)
911da177e4SLinus Torvalds 		/* delay for broken crystal part */
92989a0b24SNishanth Aravamudan 		msleep(750);
931da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1,
9465b29f50STakashi Iwai 				   chip->awacs_reg[1] | MASK_RECALIBRATE |
9565b29f50STakashi Iwai 				   MASK_CMUTE | MASK_AMUTE);
961da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
971da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds #else
1011da177e4SLinus Torvalds #define screamer_recalibrate(chip) /* NOP */
1021da177e4SLinus Torvalds #endif
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds /*
1061da177e4SLinus Torvalds  * additional callback to set the pcm format
1071da177e4SLinus Torvalds  */
10865b29f50STakashi Iwai static void snd_pmac_awacs_set_format(struct snd_pmac *chip)
1091da177e4SLinus Torvalds {
1101da177e4SLinus Torvalds 	chip->awacs_reg[1] &= ~MASK_SAMPLERATE;
1111da177e4SLinus Torvalds 	chip->awacs_reg[1] |= chip->rate_index << 3;
1121da177e4SLinus Torvalds 	snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
1131da177e4SLinus Torvalds }
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds /*
1171da177e4SLinus Torvalds  * AWACS volume callbacks
1181da177e4SLinus Torvalds  */
1191da177e4SLinus Torvalds /*
1201da177e4SLinus Torvalds  * volumes: 0-15 stereo
1211da177e4SLinus Torvalds  */
12265b29f50STakashi Iwai static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol,
12365b29f50STakashi Iwai 				      struct snd_ctl_elem_info *uinfo)
1241da177e4SLinus Torvalds {
1251da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1261da177e4SLinus Torvalds 	uinfo->count = 2;
1271da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
1281da177e4SLinus Torvalds 	uinfo->value.integer.max = 15;
1291da177e4SLinus Torvalds 	return 0;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
13265b29f50STakashi Iwai static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol,
13365b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
1341da177e4SLinus Torvalds {
13565b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1361da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
1371da177e4SLinus Torvalds 	int lshift = (kcontrol->private_value >> 8) & 0xff;
1381da177e4SLinus Torvalds 	int inverted = (kcontrol->private_value >> 16) & 1;
1391da177e4SLinus Torvalds 	unsigned long flags;
1401da177e4SLinus Torvalds 	int vol[2];
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1431da177e4SLinus Torvalds 	vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf;
1441da177e4SLinus Torvalds 	vol[1] = chip->awacs_reg[reg] & 0xf;
1451da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1461da177e4SLinus Torvalds 	if (inverted) {
1471da177e4SLinus Torvalds 		vol[0] = 0x0f - vol[0];
1481da177e4SLinus Torvalds 		vol[1] = 0x0f - vol[1];
1491da177e4SLinus Torvalds 	}
1501da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = vol[0];
1511da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = vol[1];
1521da177e4SLinus Torvalds 	return 0;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
15565b29f50STakashi Iwai static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
15665b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
1571da177e4SLinus Torvalds {
15865b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1591da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
1601da177e4SLinus Torvalds 	int lshift = (kcontrol->private_value >> 8) & 0xff;
1611da177e4SLinus Torvalds 	int inverted = (kcontrol->private_value >> 16) & 1;
1621da177e4SLinus Torvalds 	int val, oldval;
1631da177e4SLinus Torvalds 	unsigned long flags;
164d4079ac4STakashi Iwai 	unsigned int vol[2];
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	vol[0] = ucontrol->value.integer.value[0];
1671da177e4SLinus Torvalds 	vol[1] = ucontrol->value.integer.value[1];
168d4079ac4STakashi Iwai 	if (vol[0] > 0x0f || vol[1] > 0x0f)
169d4079ac4STakashi Iwai 		return -EINVAL;
1701da177e4SLinus Torvalds 	if (inverted) {
1711da177e4SLinus Torvalds 		vol[0] = 0x0f - vol[0];
1721da177e4SLinus Torvalds 		vol[1] = 0x0f - vol[1];
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds 	vol[0] &= 0x0f;
1751da177e4SLinus Torvalds 	vol[1] &= 0x0f;
1761da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1771da177e4SLinus Torvalds 	oldval = chip->awacs_reg[reg];
1781da177e4SLinus Torvalds 	val = oldval & ~(0xf | (0xf << lshift));
1791da177e4SLinus Torvalds 	val |= vol[0] << lshift;
1801da177e4SLinus Torvalds 	val |= vol[1];
1811da177e4SLinus Torvalds 	if (oldval != val)
1821da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, reg, val);
1831da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1841da177e4SLinus Torvalds 	return oldval != reg;
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds #define AWACS_VOLUME(xname, xreg, xshift, xinverted) \
1891da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
1901da177e4SLinus Torvalds   .info = snd_pmac_awacs_info_volume, \
1911da177e4SLinus Torvalds   .get = snd_pmac_awacs_get_volume, \
1921da177e4SLinus Torvalds   .put = snd_pmac_awacs_put_volume, \
1931da177e4SLinus Torvalds   .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) }
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds /*
1961da177e4SLinus Torvalds  * mute master/ogain for AWACS: mono
1971da177e4SLinus Torvalds  */
19865b29f50STakashi Iwai static int snd_pmac_awacs_get_switch(struct snd_kcontrol *kcontrol,
19965b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
2001da177e4SLinus Torvalds {
20165b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
2021da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
2031da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
2041da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 16) & 1;
2051da177e4SLinus Torvalds 	int val;
2061da177e4SLinus Torvalds 	unsigned long flags;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
2091da177e4SLinus Torvalds 	val = (chip->awacs_reg[reg] >> shift) & 1;
2101da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
2111da177e4SLinus Torvalds 	if (invert)
2121da177e4SLinus Torvalds 		val = 1 - val;
2131da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
2141da177e4SLinus Torvalds 	return 0;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
21765b29f50STakashi Iwai static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol,
21865b29f50STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
2191da177e4SLinus Torvalds {
22065b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
2211da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
2221da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
2231da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 16) & 1;
2241da177e4SLinus Torvalds 	int mask = 1 << shift;
2251da177e4SLinus Torvalds 	int val, changed;
2261da177e4SLinus Torvalds 	unsigned long flags;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
2291da177e4SLinus Torvalds 	val = chip->awacs_reg[reg] & ~mask;
2301da177e4SLinus Torvalds 	if (ucontrol->value.integer.value[0] != invert)
2311da177e4SLinus Torvalds 		val |= mask;
2321da177e4SLinus Torvalds 	changed = chip->awacs_reg[reg] != val;
2331da177e4SLinus Torvalds 	if (changed)
2341da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, reg, val);
2351da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
2361da177e4SLinus Torvalds 	return changed;
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds #define AWACS_SWITCH(xname, xreg, xshift, xinvert) \
2401da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
2411da177e4SLinus Torvalds   .info = snd_pmac_boolean_mono_info, \
2421da177e4SLinus Torvalds   .get = snd_pmac_awacs_get_switch, \
2431da177e4SLinus Torvalds   .put = snd_pmac_awacs_put_switch, \
2441da177e4SLinus Torvalds   .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) }
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
2481da177e4SLinus Torvalds /*
2491da177e4SLinus Torvalds  * controls for perch/whisper extension cards, e.g. G3 desktop
2501da177e4SLinus Torvalds  *
2511da177e4SLinus Torvalds  * TDA7433 connected via i2c address 0x45 (= 0x8a),
2521da177e4SLinus Torvalds  * accessed through cuda
2531da177e4SLinus Torvalds  */
2541da177e4SLinus Torvalds static void awacs_set_cuda(int reg, int val)
2551da177e4SLinus Torvalds {
2561da177e4SLinus Torvalds 	struct adb_request req;
2577ae44cfaSRisto Suominen 	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a,
2587ae44cfaSRisto Suominen 			reg, val);
2591da177e4SLinus Torvalds 	while (! req.complete)
2601da177e4SLinus Torvalds 		cuda_poll();
2611da177e4SLinus Torvalds }
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds /*
2641da177e4SLinus Torvalds  * level = 0 - 14, 7 = 0 dB
2651da177e4SLinus Torvalds  */
26665b29f50STakashi Iwai static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble)
2671da177e4SLinus Torvalds {
2681da177e4SLinus Torvalds 	amp->amp_tone[0] = bass;
2691da177e4SLinus Torvalds 	amp->amp_tone[1] = treble;
2701da177e4SLinus Torvalds 	if (bass > 7)
2711da177e4SLinus Torvalds 		bass = (14 - bass) + 8;
2721da177e4SLinus Torvalds 	if (treble > 7)
2731da177e4SLinus Torvalds 		treble = (14 - treble) + 8;
2741da177e4SLinus Torvalds 	awacs_set_cuda(2, (bass << 4) | treble);
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds /*
2781da177e4SLinus Torvalds  * vol = 0 - 31 (attenuation), 32 = mute bit, stereo
2791da177e4SLinus Torvalds  */
2807ae44cfaSRisto Suominen static int awacs_amp_set_vol(struct awacs_amp *amp, int index,
2817ae44cfaSRisto Suominen 			     int lvol, int rvol, int do_check)
2821da177e4SLinus Torvalds {
2831da177e4SLinus Torvalds 	if (do_check && amp->amp_vol[index][0] == lvol &&
2841da177e4SLinus Torvalds 			amp->amp_vol[index][1] == rvol)
2851da177e4SLinus Torvalds 		return 0;
2861da177e4SLinus Torvalds 	awacs_set_cuda(3 + index, lvol);
2871da177e4SLinus Torvalds 	awacs_set_cuda(5 + index, rvol);
2881da177e4SLinus Torvalds 	amp->amp_vol[index][0] = lvol;
2891da177e4SLinus Torvalds 	amp->amp_vol[index][1] = rvol;
2901da177e4SLinus Torvalds 	return 1;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds /*
2941da177e4SLinus Torvalds  * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB
2951da177e4SLinus Torvalds  */
29665b29f50STakashi Iwai static void awacs_amp_set_master(struct awacs_amp *amp, int vol)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds 	amp->amp_master = vol;
2991da177e4SLinus Torvalds 	if (vol <= 79)
3001da177e4SLinus Torvalds 		vol = 32 + (79 - vol);
3011da177e4SLinus Torvalds 	else
3021da177e4SLinus Torvalds 		vol = 32 - (vol - 79);
3031da177e4SLinus Torvalds 	awacs_set_cuda(1, vol);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
30665b29f50STakashi Iwai static void awacs_amp_free(struct snd_pmac *chip)
3071da177e4SLinus Torvalds {
30865b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3095e246b85STakashi Iwai 	if (!amp)
3105e246b85STakashi Iwai 		return;
3111da177e4SLinus Torvalds 	kfree(amp);
3121da177e4SLinus Torvalds 	chip->mixer_data = NULL;
3131da177e4SLinus Torvalds 	chip->mixer_free = NULL;
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds /*
3181da177e4SLinus Torvalds  * mixer controls
3191da177e4SLinus Torvalds  */
32065b29f50STakashi Iwai static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol,
32165b29f50STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3241da177e4SLinus Torvalds 	uinfo->count = 2;
3251da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3261da177e4SLinus Torvalds 	uinfo->value.integer.max = 31;
3271da177e4SLinus Torvalds 	return 0;
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds 
33065b29f50STakashi Iwai static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol,
33165b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3321da177e4SLinus Torvalds {
33365b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3341da177e4SLinus Torvalds 	int index = kcontrol->private_value;
33565b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3365e246b85STakashi Iwai 
3371da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31);
3381da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31);
3391da177e4SLinus Torvalds 	return 0;
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds 
34265b29f50STakashi Iwai static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol,
34365b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3441da177e4SLinus Torvalds {
34565b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3461da177e4SLinus Torvalds 	int index = kcontrol->private_value;
3471da177e4SLinus Torvalds 	int vol[2];
34865b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3491da177e4SLinus Torvalds 
3507ae44cfaSRisto Suominen 	vol[0] = (31 - (ucontrol->value.integer.value[0] & 31))
3517ae44cfaSRisto Suominen 		| (amp->amp_vol[index][0] & 32);
3527ae44cfaSRisto Suominen 	vol[1] = (31 - (ucontrol->value.integer.value[1] & 31))
3537ae44cfaSRisto Suominen 		| (amp->amp_vol[index][1] & 32);
3541da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
35765b29f50STakashi Iwai static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol,
35865b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3591da177e4SLinus Torvalds {
36065b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3611da177e4SLinus Torvalds 	int index = kcontrol->private_value;
36265b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3635e246b85STakashi Iwai 
3647ae44cfaSRisto Suominen 	ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32)
3657ae44cfaSRisto Suominen 					? 0 : 1;
3667ae44cfaSRisto Suominen 	ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32)
3677ae44cfaSRisto Suominen 					? 0 : 1;
3681da177e4SLinus Torvalds 	return 0;
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds 
37165b29f50STakashi Iwai static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol,
37265b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
3731da177e4SLinus Torvalds {
37465b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
3751da177e4SLinus Torvalds 	int index = kcontrol->private_value;
3761da177e4SLinus Torvalds 	int vol[2];
37765b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
3781da177e4SLinus Torvalds 
3797ae44cfaSRisto Suominen 	vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32)
3807ae44cfaSRisto Suominen 		| (amp->amp_vol[index][0] & 31);
3817ae44cfaSRisto Suominen 	vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32)
3827ae44cfaSRisto Suominen 		| (amp->amp_vol[index][1] & 31);
3831da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds 
38665b29f50STakashi Iwai static int snd_pmac_awacs_info_tone_amp(struct snd_kcontrol *kcontrol,
38765b29f50STakashi Iwai 					struct snd_ctl_elem_info *uinfo)
3881da177e4SLinus Torvalds {
3891da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3901da177e4SLinus Torvalds 	uinfo->count = 1;
3911da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3921da177e4SLinus Torvalds 	uinfo->value.integer.max = 14;
3931da177e4SLinus Torvalds 	return 0;
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds 
39665b29f50STakashi Iwai static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol,
39765b29f50STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
3981da177e4SLinus Torvalds {
39965b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
4001da177e4SLinus Torvalds 	int index = kcontrol->private_value;
40165b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
4025e246b85STakashi Iwai 
4031da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = amp->amp_tone[index];
4041da177e4SLinus Torvalds 	return 0;
4051da177e4SLinus Torvalds }
4061da177e4SLinus Torvalds 
40765b29f50STakashi Iwai static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol,
40865b29f50STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
4091da177e4SLinus Torvalds {
41065b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
4111da177e4SLinus Torvalds 	int index = kcontrol->private_value;
41265b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
413d4079ac4STakashi Iwai 	unsigned int val;
4145e246b85STakashi Iwai 
415d4079ac4STakashi Iwai 	val = ucontrol->value.integer.value[0];
416d4079ac4STakashi Iwai 	if (val > 14)
417d4079ac4STakashi Iwai 		return -EINVAL;
418d4079ac4STakashi Iwai 	if (val != amp->amp_tone[index]) {
419d4079ac4STakashi Iwai 		amp->amp_tone[index] = val;
4201da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
4211da177e4SLinus Torvalds 		return 1;
4221da177e4SLinus Torvalds 	}
4231da177e4SLinus Torvalds 	return 0;
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds 
42665b29f50STakashi Iwai static int snd_pmac_awacs_info_master_amp(struct snd_kcontrol *kcontrol,
42765b29f50STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4301da177e4SLinus Torvalds 	uinfo->count = 1;
4311da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
4321da177e4SLinus Torvalds 	uinfo->value.integer.max = 99;
4331da177e4SLinus Torvalds 	return 0;
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds 
43665b29f50STakashi Iwai static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol,
43765b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
4381da177e4SLinus Torvalds {
43965b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
44065b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
4415e246b85STakashi Iwai 
4421da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = amp->amp_master;
4431da177e4SLinus Torvalds 	return 0;
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds 
44665b29f50STakashi Iwai static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
44765b29f50STakashi Iwai 					 struct snd_ctl_elem_value *ucontrol)
4481da177e4SLinus Torvalds {
44965b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
45065b29f50STakashi Iwai 	struct awacs_amp *amp = chip->mixer_data;
451d4079ac4STakashi Iwai 	unsigned int val;
4525e246b85STakashi Iwai 
453d4079ac4STakashi Iwai 	val = ucontrol->value.integer.value[0];
454d4079ac4STakashi Iwai 	if (val > 99)
455d4079ac4STakashi Iwai 		return -EINVAL;
456d4079ac4STakashi Iwai 	if (val != amp->amp_master) {
457d4079ac4STakashi Iwai 		amp->amp_master = val;
4581da177e4SLinus Torvalds 		awacs_amp_set_master(amp, amp->amp_master);
4591da177e4SLinus Torvalds 		return 1;
4601da177e4SLinus Torvalds 	}
4611da177e4SLinus Torvalds 	return 0;
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds #define AMP_CH_SPK	0
4651da177e4SLinus Torvalds #define AMP_CH_HD	1
4661da177e4SLinus Torvalds 
467c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] = {
4681da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
469ad1cd745SJaroslav Kysela 	  .name = "Speaker Playback Volume",
4701da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_volume_amp,
4711da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_volume_amp,
4721da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_volume_amp,
4731da177e4SLinus Torvalds 	  .private_value = AMP_CH_SPK,
4741da177e4SLinus Torvalds 	},
4751da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4761da177e4SLinus Torvalds 	  .name = "Headphone Playback Volume",
4771da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_volume_amp,
4781da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_volume_amp,
4791da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_volume_amp,
4801da177e4SLinus Torvalds 	  .private_value = AMP_CH_HD,
4811da177e4SLinus Torvalds 	},
4821da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4831da177e4SLinus Torvalds 	  .name = "Tone Control - Bass",
4841da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_tone_amp,
4851da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_tone_amp,
4861da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_tone_amp,
4871da177e4SLinus Torvalds 	  .private_value = 0,
4881da177e4SLinus Torvalds 	},
4891da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4901da177e4SLinus Torvalds 	  .name = "Tone Control - Treble",
4911da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_tone_amp,
4921da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_tone_amp,
4931da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_tone_amp,
4941da177e4SLinus Torvalds 	  .private_value = 1,
4951da177e4SLinus Torvalds 	},
4961da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4971da177e4SLinus Torvalds 	  .name = "Amp Master Playback Volume",
4981da177e4SLinus Torvalds 	  .info = snd_pmac_awacs_info_master_amp,
4991da177e4SLinus Torvalds 	  .get = snd_pmac_awacs_get_master_amp,
5001da177e4SLinus Torvalds 	  .put = snd_pmac_awacs_put_master_amp,
5011da177e4SLinus Torvalds 	},
5021da177e4SLinus Torvalds };
5031da177e4SLinus Torvalds 
504905e46acSBhumika Goyal static const struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = {
5051da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5061da177e4SLinus Torvalds 	.name = "Headphone Playback Switch",
5071da177e4SLinus Torvalds 	.info = snd_pmac_boolean_stereo_info,
5081da177e4SLinus Torvalds 	.get = snd_pmac_awacs_get_switch_amp,
5091da177e4SLinus Torvalds 	.put = snd_pmac_awacs_put_switch_amp,
5101da177e4SLinus Torvalds 	.private_value = AMP_CH_HD,
5111da177e4SLinus Torvalds };
5121da177e4SLinus Torvalds 
513905e46acSBhumika Goyal static const struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw = {
5141da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
515ad1cd745SJaroslav Kysela 	.name = "Speaker Playback Switch",
5161da177e4SLinus Torvalds 	.info = snd_pmac_boolean_stereo_info,
5171da177e4SLinus Torvalds 	.get = snd_pmac_awacs_get_switch_amp,
5181da177e4SLinus Torvalds 	.put = snd_pmac_awacs_put_switch_amp,
5191da177e4SLinus Torvalds 	.private_value = AMP_CH_SPK,
5201da177e4SLinus Torvalds };
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds /*
5261da177e4SLinus Torvalds  * mic boost for screamer
5271da177e4SLinus Torvalds  */
52865b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol,
52965b29f50STakashi Iwai 					    struct snd_ctl_elem_info *uinfo)
5301da177e4SLinus Torvalds {
5311da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
5321da177e4SLinus Torvalds 	uinfo->count = 1;
5331da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
534a8c2a6bfSRisto Suominen 	uinfo->value.integer.max = 3;
5351da177e4SLinus Torvalds 	return 0;
5361da177e4SLinus Torvalds }
5371da177e4SLinus Torvalds 
53865b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol,
53965b29f50STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
5401da177e4SLinus Torvalds {
54165b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
542a8c2a6bfSRisto Suominen 	int val = 0;
5431da177e4SLinus Torvalds 	unsigned long flags;
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
5461da177e4SLinus Torvalds 	if (chip->awacs_reg[6] & MASK_MIC_BOOST)
547a8c2a6bfSRisto Suominen 		val |= 2;
548a8c2a6bfSRisto Suominen 	if (chip->awacs_reg[0] & MASK_GAINLINE)
549a8c2a6bfSRisto Suominen 		val |= 1;
5501da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
5511da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
5521da177e4SLinus Torvalds 	return 0;
5531da177e4SLinus Torvalds }
5541da177e4SLinus Torvalds 
55565b29f50STakashi Iwai static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
55665b29f50STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
5571da177e4SLinus Torvalds {
55865b29f50STakashi Iwai 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
5591da177e4SLinus Torvalds 	int changed = 0;
5601da177e4SLinus Torvalds 	int val0, val6;
5611da177e4SLinus Torvalds 	unsigned long flags;
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
5641da177e4SLinus Torvalds 	val0 = chip->awacs_reg[0] & ~MASK_GAINLINE;
5651da177e4SLinus Torvalds 	val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST;
566a8c2a6bfSRisto Suominen 	if (ucontrol->value.integer.value[0] & 1)
5671da177e4SLinus Torvalds 		val0 |= MASK_GAINLINE;
568a8c2a6bfSRisto Suominen 	if (ucontrol->value.integer.value[0] & 2)
5691da177e4SLinus Torvalds 		val6 |= MASK_MIC_BOOST;
5701da177e4SLinus Torvalds 	if (val0 != chip->awacs_reg[0]) {
5711da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 0, val0);
5721da177e4SLinus Torvalds 		changed = 1;
5731da177e4SLinus Torvalds 	}
5741da177e4SLinus Torvalds 	if (val6 != chip->awacs_reg[6]) {
5751da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 6, val6);
5761da177e4SLinus Torvalds 		changed = 1;
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
5791da177e4SLinus Torvalds 	return changed;
5801da177e4SLinus Torvalds }
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds /*
5831da177e4SLinus Torvalds  * lists of mixer elements
5841da177e4SLinus Torvalds  */
585c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers[] = {
5861da177e4SLinus Torvalds 	AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
587a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Capture Volume", 0, 4, 0),
588a8c2a6bfSRisto Suominen /*	AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */
589a8c2a6bfSRisto Suominen };
590a8c2a6bfSRisto Suominen 
591c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] = {
592a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
593a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
594a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
595a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
596a8c2a6bfSRisto Suominen };
597a8c2a6bfSRisto Suominen 
598c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] = {
599a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
600dca7c741SRisto Suominen };
601dca7c741SRisto Suominen 
602c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] = {
603dca7c741SRisto Suominen 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
604a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
605a8c2a6bfSRisto Suominen };
606a8c2a6bfSRisto Suominen 
607c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] = {
6084dbf95baSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
6094dbf95baSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
6104dbf95baSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
6114dbf95baSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
6124dbf95baSRisto Suominen };
6134dbf95baSRisto Suominen 
614c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] = {
615a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
616a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
617a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
618a8c2a6bfSRisto Suominen };
619a8c2a6bfSRisto Suominen 
620c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] = {
621dca7c741SRisto Suominen 	AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1),
622dca7c741SRisto Suominen };
623dca7c741SRisto Suominen 
624c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] = {
625a8c2a6bfSRisto Suominen 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
6261da177e4SLinus Torvalds 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
6271da177e4SLinus Torvalds };
6281da177e4SLinus Torvalds 
6291da177e4SLinus Torvalds /* FIXME: is this correct order?
6301da177e4SLinus Torvalds  * screamer (powerbook G3 pismo) seems to have different bits...
6311da177e4SLinus Torvalds  */
632c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers2[] = {
6331da177e4SLinus Torvalds 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
6341da177e4SLinus Torvalds 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
6351da177e4SLinus Torvalds };
6361da177e4SLinus Torvalds 
637c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mixers2[] = {
6381da177e4SLinus Torvalds 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
6391da177e4SLinus Torvalds 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
6401da177e4SLinus Torvalds };
6411da177e4SLinus Torvalds 
642c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] = {
643dca7c741SRisto Suominen 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
644dca7c741SRisto Suominen };
645dca7c741SRisto Suominen 
646c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_master_sw =
6471da177e4SLinus Torvalds AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
6481da177e4SLinus Torvalds 
649c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac =
650a8c2a6bfSRisto Suominen AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
651a8c2a6bfSRisto Suominen 
652c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 =
653dca7c741SRisto Suominen AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1);
654dca7c741SRisto Suominen 
655c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] = {
656a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
6571da177e4SLinus Torvalds };
6581da177e4SLinus Torvalds 
659c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] = {
6601da177e4SLinus Torvalds 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
661a8c2a6bfSRisto Suominen 	  .name = "Mic Boost Capture Volume",
6621da177e4SLinus Torvalds 	  .info = snd_pmac_screamer_mic_boost_info,
6631da177e4SLinus Torvalds 	  .get = snd_pmac_screamer_mic_boost_get,
6641da177e4SLinus Torvalds 	  .put = snd_pmac_screamer_mic_boost_put,
6651da177e4SLinus Torvalds 	},
6661da177e4SLinus Torvalds };
6671da177e4SLinus Torvalds 
668c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] =
669a8c2a6bfSRisto Suominen {
670a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
671a8c2a6bfSRisto Suominen };
672a8c2a6bfSRisto Suominen 
673c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] =
674a8c2a6bfSRisto Suominen {
675a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
676a8c2a6bfSRisto Suominen 	AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
677a8c2a6bfSRisto Suominen };
678a8c2a6bfSRisto Suominen 
679c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] =
680a8c2a6bfSRisto Suominen {
681a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
682a8c2a6bfSRisto Suominen 	AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
683a8c2a6bfSRisto Suominen };
684a8c2a6bfSRisto Suominen 
685c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] = {
686ad1cd745SJaroslav Kysela 	AWACS_VOLUME("Speaker Playback Volume", 4, 6, 1),
6871da177e4SLinus Torvalds };
688a8c2a6bfSRisto Suominen 
689c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw =
690ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
6911da177e4SLinus Torvalds 
692c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 =
693ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
694030b655bSRisto Suominen 
695c031b0ccSTakashi Iwai static const struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 =
696ad1cd745SJaroslav Kysela AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
697a8c2a6bfSRisto Suominen 
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds /*
7001da177e4SLinus Torvalds  * add new mixer elements to the card
7011da177e4SLinus Torvalds  */
7027ae44cfaSRisto Suominen static int build_mixers(struct snd_pmac *chip, int nums,
703c031b0ccSTakashi Iwai 			const struct snd_kcontrol_new *mixers)
7041da177e4SLinus Torvalds {
7051da177e4SLinus Torvalds 	int i, err;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	for (i = 0; i < nums; i++) {
7087ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip));
7097ae44cfaSRisto Suominen 		if (err < 0)
7101da177e4SLinus Torvalds 			return err;
7111da177e4SLinus Torvalds 	}
7121da177e4SLinus Torvalds 	return 0;
7131da177e4SLinus Torvalds }
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds /*
7171da177e4SLinus Torvalds  * restore all registers
7181da177e4SLinus Torvalds  */
71965b29f50STakashi Iwai static void awacs_restore_all_regs(struct snd_pmac *chip)
7201da177e4SLinus Torvalds {
7211da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
7221da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
7231da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]);
7241da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]);
7251da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
7261da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]);
7271da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
7281da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]);
7291da177e4SLinus Torvalds 	}
7301da177e4SLinus Torvalds }
7311da177e4SLinus Torvalds 
7328c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
73365b29f50STakashi Iwai static void snd_pmac_awacs_suspend(struct snd_pmac *chip)
7341da177e4SLinus Torvalds {
7351da177e4SLinus Torvalds 	snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1]
7361da177e4SLinus Torvalds 					     | MASK_AMUTE | MASK_CMUTE));
7371da177e4SLinus Torvalds }
7381da177e4SLinus Torvalds 
73965b29f50STakashi Iwai static void snd_pmac_awacs_resume(struct snd_pmac *chip)
7401da177e4SLinus Torvalds {
74171a157e8SGrant Likely 	if (of_machine_is_compatible("PowerBook3,1")
74271a157e8SGrant Likely 	    || of_machine_is_compatible("PowerBook3,2")) {
743989a0b24SNishanth Aravamudan 		msleep(100);
7441da177e4SLinus Torvalds 		snd_pmac_awacs_write_reg(chip, 1,
7451da177e4SLinus Torvalds 			chip->awacs_reg[1] & ~MASK_PAROUT);
746989a0b24SNishanth Aravamudan 		msleep(300);
7471da177e4SLinus Torvalds 	}
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds 	awacs_restore_all_regs(chip);
7501da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
7511da177e4SLinus Torvalds 		/* reset power bits in reg 6 */
7521da177e4SLinus Torvalds 		mdelay(5);
7531da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
7541da177e4SLinus Torvalds 	}
7551da177e4SLinus Torvalds 	screamer_recalibrate(chip);
7561da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
7571da177e4SLinus Torvalds 	if (chip->mixer_data) {
75865b29f50STakashi Iwai 		struct awacs_amp *amp = chip->mixer_data;
7597ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 0,
7607ae44cfaSRisto Suominen 				  amp->amp_vol[0][0], amp->amp_vol[0][1], 0);
7617ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 1,
7627ae44cfaSRisto Suominen 				  amp->amp_vol[1][0], amp->amp_vol[1][1], 0);
7631da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
7641da177e4SLinus Torvalds 		awacs_amp_set_master(amp, amp->amp_master);
7651da177e4SLinus Torvalds 	}
7661da177e4SLinus Torvalds #endif
7671da177e4SLinus Torvalds }
7688c870933SBenjamin Herrenschmidt #endif /* CONFIG_PM */
7691da177e4SLinus Torvalds 
77071a157e8SGrant Likely #define IS_PM7500 (of_machine_is_compatible("AAPL,7500") \
77171a157e8SGrant Likely 		|| of_machine_is_compatible("AAPL,8500") \
77271a157e8SGrant Likely 		|| of_machine_is_compatible("AAPL,9500"))
77371a157e8SGrant Likely #define IS_PM5500 (of_machine_is_compatible("AAPL,e411"))
77471a157e8SGrant Likely #define IS_BEIGE (of_machine_is_compatible("AAPL,Gossamer"))
77571a157e8SGrant Likely #define IS_IMAC1 (of_machine_is_compatible("PowerMac2,1"))
77671a157e8SGrant Likely #define IS_IMAC2 (of_machine_is_compatible("PowerMac2,2") \
77771a157e8SGrant Likely 		|| of_machine_is_compatible("PowerMac4,1"))
77871a157e8SGrant Likely #define IS_G4AGP (of_machine_is_compatible("PowerMac3,1"))
77971a157e8SGrant Likely #define IS_LOMBARD (of_machine_is_compatible("PowerBook1,1"))
780a8c2a6bfSRisto Suominen 
781030b655bSRisto Suominen static int imac1, imac2;
782a8c2a6bfSRisto Suominen 
7831da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE
7841da177e4SLinus Torvalds /*
7851da177e4SLinus Torvalds  * auto-mute stuffs
7861da177e4SLinus Torvalds  */
78765b29f50STakashi Iwai static int snd_pmac_awacs_detect_headphone(struct snd_pmac *chip)
7881da177e4SLinus Torvalds {
7891da177e4SLinus Torvalds 	return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0;
7901da177e4SLinus Torvalds }
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
79365b29f50STakashi Iwai static int toggle_amp_mute(struct awacs_amp *amp, int index, int mute)
7941da177e4SLinus Torvalds {
7951da177e4SLinus Torvalds 	int vol[2];
7961da177e4SLinus Torvalds 	vol[0] = amp->amp_vol[index][0] & 31;
7971da177e4SLinus Torvalds 	vol[1] = amp->amp_vol[index][1] & 31;
7981da177e4SLinus Torvalds 	if (mute) {
7991da177e4SLinus Torvalds 		vol[0] |= 32;
8001da177e4SLinus Torvalds 		vol[1] |= 32;
8011da177e4SLinus Torvalds 	}
8021da177e4SLinus Torvalds 	return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
8031da177e4SLinus Torvalds }
8041da177e4SLinus Torvalds #endif
8051da177e4SLinus Torvalds 
80665b29f50STakashi Iwai static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)
8071da177e4SLinus Torvalds {
8081da177e4SLinus Torvalds 	if (chip->auto_mute) {
8091da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
8101da177e4SLinus Torvalds 		if (chip->mixer_data) {
81165b29f50STakashi Iwai 			struct awacs_amp *amp = chip->mixer_data;
8121da177e4SLinus Torvalds 			int changed;
8131da177e4SLinus Torvalds 			if (snd_pmac_awacs_detect_headphone(chip)) {
8141da177e4SLinus Torvalds 				changed = toggle_amp_mute(amp, AMP_CH_HD, 0);
8151da177e4SLinus Torvalds 				changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1);
8161da177e4SLinus Torvalds 			} else {
8171da177e4SLinus Torvalds 				changed = toggle_amp_mute(amp, AMP_CH_HD, 1);
8181da177e4SLinus Torvalds 				changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0);
8191da177e4SLinus Torvalds 			}
8201da177e4SLinus Torvalds 			if (do_notify && ! changed)
8211da177e4SLinus Torvalds 				return;
8221da177e4SLinus Torvalds 		} else
8231da177e4SLinus Torvalds #endif
8241da177e4SLinus Torvalds 		{
825a8c2a6bfSRisto Suominen 			int reg = chip->awacs_reg[1]
826a8c2a6bfSRisto Suominen 				| (MASK_HDMUTE | MASK_SPKMUTE);
827030b655bSRisto Suominen 			if (imac1) {
828030b655bSRisto Suominen 				reg &= ~MASK_SPKMUTE;
829030b655bSRisto Suominen 				reg |= MASK_PAROUT1;
830030b655bSRisto Suominen 			} else if (imac2) {
831a8c2a6bfSRisto Suominen 				reg &= ~MASK_SPKMUTE;
832a8c2a6bfSRisto Suominen 				reg &= ~MASK_PAROUT1;
833a8c2a6bfSRisto Suominen 			}
8341da177e4SLinus Torvalds 			if (snd_pmac_awacs_detect_headphone(chip))
8351da177e4SLinus Torvalds 				reg &= ~MASK_HDMUTE;
836030b655bSRisto Suominen 			else if (imac1)
837030b655bSRisto Suominen 				reg &= ~MASK_PAROUT1;
838030b655bSRisto Suominen 			else if (imac2)
839a8c2a6bfSRisto Suominen 				reg |= MASK_PAROUT1;
8401da177e4SLinus Torvalds 			else
8411da177e4SLinus Torvalds 				reg &= ~MASK_SPKMUTE;
8421da177e4SLinus Torvalds 			if (do_notify && reg == chip->awacs_reg[1])
8431da177e4SLinus Torvalds 				return;
8441da177e4SLinus Torvalds 			snd_pmac_awacs_write_reg(chip, 1, reg);
8451da177e4SLinus Torvalds 		}
8461da177e4SLinus Torvalds 		if (do_notify) {
8471da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8481da177e4SLinus Torvalds 				       &chip->master_sw_ctl->id);
8491da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8501da177e4SLinus Torvalds 				       &chip->speaker_sw_ctl->id);
8511da177e4SLinus Torvalds 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
8521da177e4SLinus Torvalds 				       &chip->hp_detect_ctl->id);
8531da177e4SLinus Torvalds 		}
8541da177e4SLinus Torvalds 	}
8551da177e4SLinus Torvalds }
8561da177e4SLinus Torvalds #endif /* PMAC_SUPPORT_AUTOMUTE */
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds /*
8601da177e4SLinus Torvalds  * initialize chip
8611da177e4SLinus Torvalds  */
86215afafc2SBill Pemberton int
86365b29f50STakashi Iwai snd_pmac_awacs_init(struct snd_pmac *chip)
8641da177e4SLinus Torvalds {
865a8c2a6bfSRisto Suominen 	int pm7500 = IS_PM7500;
866b0a8a8fdSRisto Suominen 	int pm5500 = IS_PM5500;
867a8c2a6bfSRisto Suominen 	int beige = IS_BEIGE;
8684dbf95baSRisto Suominen 	int g4agp = IS_G4AGP;
869573934bcSRisto Suominen 	int lombard = IS_LOMBARD;
870030b655bSRisto Suominen 	int imac;
8711da177e4SLinus Torvalds 	int err, vol;
872dca7c741SRisto Suominen 	struct snd_kcontrol *vmaster_sw, *vmaster_vol;
873dca7c741SRisto Suominen 	struct snd_kcontrol *master_vol, *speaker_vol;
8741da177e4SLinus Torvalds 
875030b655bSRisto Suominen 	imac1 = IS_IMAC1;
876030b655bSRisto Suominen 	imac2 = IS_IMAC2;
877030b655bSRisto Suominen 	imac = imac1 || imac2;
8781da177e4SLinus Torvalds 	/* looks like MASK_GAINLINE triggers something, so we set here
8791da177e4SLinus Torvalds 	 * as start-up
8801da177e4SLinus Torvalds 	 */
8811da177e4SLinus Torvalds 	chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE;
8821da177e4SLinus Torvalds 	chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE;
8831da177e4SLinus Torvalds 	/* FIXME: Only machines with external SRS module need MASK_PAROUT */
8841da177e4SLinus Torvalds 	if (chip->has_iic || chip->device_id == 0x5 ||
8851da177e4SLinus Torvalds 	    /* chip->_device_id == 0x8 || */
8861da177e4SLinus Torvalds 	    chip->device_id == 0xb)
8871da177e4SLinus Torvalds 		chip->awacs_reg[1] |= MASK_PAROUT;
8881da177e4SLinus Torvalds 	/* get default volume from nvram */
8891da177e4SLinus Torvalds 	// vol = (~nvram_read_byte(0x1308) & 7) << 1;
8901da177e4SLinus Torvalds 	// vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
8911da177e4SLinus Torvalds 	vol = 0x0f; /* no, on alsa, muted as default */
8921da177e4SLinus Torvalds 	vol = vol + (vol << 6);
8931da177e4SLinus Torvalds 	chip->awacs_reg[2] = vol;
8941da177e4SLinus Torvalds 	chip->awacs_reg[4] = vol;
8951da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
8967ae44cfaSRisto Suominen 		/* FIXME: screamer has loopthru vol control */
8977ae44cfaSRisto Suominen 		chip->awacs_reg[5] = vol;
8987ae44cfaSRisto Suominen 		/* FIXME: maybe should be vol << 3 for PCMCIA speaker */
8997ae44cfaSRisto Suominen 		chip->awacs_reg[6] = MASK_MIC_BOOST;
9001da177e4SLinus Torvalds 		chip->awacs_reg[7] = 0;
9011da177e4SLinus Torvalds 	}
9021da177e4SLinus Torvalds 
9031da177e4SLinus Torvalds 	awacs_restore_all_regs(chip);
9041da177e4SLinus Torvalds 	chip->manufacturer = (in_le32(&chip->awacs->codec_stat) >> 8) & 0xf;
9051da177e4SLinus Torvalds 	screamer_recalibrate(chip);
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 	chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;
9081da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
9091da177e4SLinus Torvalds 	if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) {
91059feddb2SPanagiotis Issaris 		struct awacs_amp *amp = kzalloc(sizeof(*amp), GFP_KERNEL);
9111da177e4SLinus Torvalds 		if (! amp)
9121da177e4SLinus Torvalds 			return -ENOMEM;
9131da177e4SLinus Torvalds 		chip->mixer_data = amp;
9141da177e4SLinus Torvalds 		chip->mixer_free = awacs_amp_free;
9157ae44cfaSRisto Suominen 		/* mute and zero vol */
9167ae44cfaSRisto Suominen 		awacs_amp_set_vol(amp, 0, 63, 63, 0);
9171da177e4SLinus Torvalds 		awacs_amp_set_vol(amp, 1, 63, 63, 0);
9181da177e4SLinus Torvalds 		awacs_amp_set_tone(amp, 7, 7); /* 0 dB */
9191da177e4SLinus Torvalds 		awacs_amp_set_master(amp, 79); /* 0 dB */
9201da177e4SLinus Torvalds 	}
9211da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 	if (chip->hp_stat_mask == 0) {
9241da177e4SLinus Torvalds 		/* set headphone-jack detection bit */
9251da177e4SLinus Torvalds 		switch (chip->model) {
9261da177e4SLinus Torvalds 		case PMAC_AWACS:
927b0a8a8fdSRisto Suominen 			chip->hp_stat_mask = pm7500 || pm5500 ? MASK_HDPCONN
928a8c2a6bfSRisto Suominen 				: MASK_LOCONN;
9291da177e4SLinus Torvalds 			break;
9301da177e4SLinus Torvalds 		case PMAC_SCREAMER:
9311da177e4SLinus Torvalds 			switch (chip->device_id) {
9321da177e4SLinus Torvalds 			case 0x08:
933a8c2a6bfSRisto Suominen 			case 0x0B:
934a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = imac
935a8c2a6bfSRisto Suominen 					? MASK_LOCONN_IMAC |
936a8c2a6bfSRisto Suominen 					MASK_HDPLCONN_IMAC |
937a8c2a6bfSRisto Suominen 					MASK_HDPRCONN_IMAC
938a8c2a6bfSRisto Suominen 					: MASK_HDPCONN;
9391da177e4SLinus Torvalds 				break;
9401da177e4SLinus Torvalds 			case 0x00:
9411da177e4SLinus Torvalds 			case 0x05:
942a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = MASK_LOCONN;
9431da177e4SLinus Torvalds 				break;
9441da177e4SLinus Torvalds 			default:
945a8c2a6bfSRisto Suominen 				chip->hp_stat_mask = MASK_HDPCONN;
9461da177e4SLinus Torvalds 				break;
9471da177e4SLinus Torvalds 			}
9481da177e4SLinus Torvalds 			break;
9491da177e4SLinus Torvalds 		default:
9501da177e4SLinus Torvalds 			snd_BUG();
9511da177e4SLinus Torvalds 			break;
9521da177e4SLinus Torvalds 		}
9531da177e4SLinus Torvalds 	}
9541da177e4SLinus Torvalds 
9551da177e4SLinus Torvalds 	/*
9561da177e4SLinus Torvalds 	 * build mixers
9571da177e4SLinus Torvalds 	 */
9581da177e4SLinus Torvalds 	strcpy(chip->card->mixername, "PowerMac AWACS");
9591da177e4SLinus Torvalds 
9607ae44cfaSRisto Suominen 	err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers),
9617ae44cfaSRisto Suominen 				snd_pmac_awacs_mixers);
9627ae44cfaSRisto Suominen 	if (err < 0)
9631da177e4SLinus Torvalds 		return err;
9644dbf95baSRisto Suominen 	if (beige || g4agp)
965a8c2a6bfSRisto Suominen 		;
966b0a8a8fdSRisto Suominen 	else if (chip->model == PMAC_SCREAMER || pm5500)
9671da177e4SLinus Torvalds 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2),
9681da177e4SLinus Torvalds 				   snd_pmac_screamer_mixers2);
969a8c2a6bfSRisto Suominen 	else if (!pm7500)
9701da177e4SLinus Torvalds 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2),
9711da177e4SLinus Torvalds 				   snd_pmac_awacs_mixers2);
9721da177e4SLinus Torvalds 	if (err < 0)
9731da177e4SLinus Torvalds 		return err;
974dca7c741SRisto Suominen 	if (pm5500) {
975dca7c741SRisto Suominen 		err = build_mixers(chip,
976dca7c741SRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers2_pmac5500),
977dca7c741SRisto Suominen 				   snd_pmac_awacs_mixers2_pmac5500);
978dca7c741SRisto Suominen 		if (err < 0)
979dca7c741SRisto Suominen 			return err;
980dca7c741SRisto Suominen 	}
981b268c34eSArnd Bergmann 	master_vol = NULL;
982a8c2a6bfSRisto Suominen 	if (pm7500)
983a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
984a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
985a8c2a6bfSRisto Suominen 				   snd_pmac_awacs_mixers_pmac7500);
986dca7c741SRisto Suominen 	else if (pm5500)
987dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
988dca7c741SRisto Suominen 		    (master_vol = snd_ctl_new1(snd_pmac_awacs_mixers_pmac5500,
989dca7c741SRisto Suominen 						chip)));
990a8c2a6bfSRisto Suominen 	else if (beige)
991a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
992a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_beige),
993a8c2a6bfSRisto Suominen 				   snd_pmac_screamer_mixers_beige);
994dca7c741SRisto Suominen 	else if (imac || lombard) {
995dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
996dca7c741SRisto Suominen 		    (master_vol = snd_ctl_new1(snd_pmac_screamer_mixers_lo,
997dca7c741SRisto Suominen 						chip)));
998dca7c741SRisto Suominen 		if (err < 0)
999dca7c741SRisto Suominen 			return err;
1000a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1001a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_imac),
1002a8c2a6bfSRisto Suominen 				   snd_pmac_screamer_mixers_imac);
1003dca7c741SRisto Suominen 	} else if (g4agp)
10044dbf95baSRisto Suominen 		err = build_mixers(chip,
10054dbf95baSRisto Suominen 				   ARRAY_SIZE(snd_pmac_screamer_mixers_g4agp),
10064dbf95baSRisto Suominen 				   snd_pmac_screamer_mixers_g4agp);
1007a8c2a6bfSRisto Suominen 	else
1008a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1009a8c2a6bfSRisto Suominen 				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac),
1010a8c2a6bfSRisto Suominen 				   snd_pmac_awacs_mixers_pmac);
1011a8c2a6bfSRisto Suominen 	if (err < 0)
1012a8c2a6bfSRisto Suominen 		return err;
1013573934bcSRisto Suominen 	chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp || lombard)
1014a8c2a6bfSRisto Suominen 			? &snd_pmac_awacs_master_sw_imac
1015dca7c741SRisto Suominen 			: pm5500
1016dca7c741SRisto Suominen 			? &snd_pmac_awacs_master_sw_pmac5500
1017a8c2a6bfSRisto Suominen 			: &snd_pmac_awacs_master_sw, chip);
10187ae44cfaSRisto Suominen 	err = snd_ctl_add(chip->card, chip->master_sw_ctl);
10197ae44cfaSRisto Suominen 	if (err < 0)
10201da177e4SLinus Torvalds 		return err;
10211da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL
10221da177e4SLinus Torvalds 	if (chip->mixer_data) {
10231da177e4SLinus Torvalds 		/* use amplifier.  the signal is connected from route A
10241da177e4SLinus Torvalds 		 * to the amp.  the amp has its headphone and speaker
10251da177e4SLinus Torvalds 		 * volumes and mute switches, so we use them instead of
10261da177e4SLinus Torvalds 		 * screamer registers.
10271da177e4SLinus Torvalds 		 * in this case, it seems the route C is not used.
10281da177e4SLinus Torvalds 		 */
10297ae44cfaSRisto Suominen 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol),
10307ae44cfaSRisto Suominen 					snd_pmac_awacs_amp_vol);
10317ae44cfaSRisto Suominen 		if (err < 0)
10321da177e4SLinus Torvalds 			return err;
10331da177e4SLinus Torvalds 		/* overwrite */
10347ae44cfaSRisto Suominen 		chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw,
10357ae44cfaSRisto Suominen 							chip);
10367ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->master_sw_ctl);
10377ae44cfaSRisto Suominen 		if (err < 0)
10381da177e4SLinus Torvalds 			return err;
10397ae44cfaSRisto Suominen 		chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw,
10407ae44cfaSRisto Suominen 							chip);
10417ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
10427ae44cfaSRisto Suominen 		if (err < 0)
10431da177e4SLinus Torvalds 			return err;
10441da177e4SLinus Torvalds 	} else
10451da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */
10461da177e4SLinus Torvalds 	{
10471da177e4SLinus Torvalds 		/* route A = headphone, route C = speaker */
1048dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card,
1049dca7c741SRisto Suominen 		    (speaker_vol = snd_ctl_new1(snd_pmac_awacs_speaker_vol,
1050dca7c741SRisto Suominen 						chip)));
10517ae44cfaSRisto Suominen 		if (err < 0)
10521da177e4SLinus Torvalds 			return err;
1053030b655bSRisto Suominen 		chip->speaker_sw_ctl = snd_ctl_new1(imac1
1054030b655bSRisto Suominen 				? &snd_pmac_awacs_speaker_sw_imac1
1055030b655bSRisto Suominen 				: imac2
1056030b655bSRisto Suominen 				? &snd_pmac_awacs_speaker_sw_imac2
1057a8c2a6bfSRisto Suominen 				: &snd_pmac_awacs_speaker_sw, chip);
10587ae44cfaSRisto Suominen 		err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
10597ae44cfaSRisto Suominen 		if (err < 0)
10601da177e4SLinus Torvalds 			return err;
10611da177e4SLinus Torvalds 	}
10621da177e4SLinus Torvalds 
1063dca7c741SRisto Suominen 	if (pm5500 || imac || lombard) {
1064dca7c741SRisto Suominen 		vmaster_sw = snd_ctl_make_virtual_master(
1065dca7c741SRisto Suominen 			"Master Playback Switch", (unsigned int *) NULL);
1066dca7c741SRisto Suominen 		err = snd_ctl_add_slave_uncached(vmaster_sw,
1067dca7c741SRisto Suominen 						 chip->master_sw_ctl);
1068dca7c741SRisto Suominen 		if (err < 0)
1069dca7c741SRisto Suominen 			return err;
1070dca7c741SRisto Suominen 		err = snd_ctl_add_slave_uncached(vmaster_sw,
1071dca7c741SRisto Suominen 						  chip->speaker_sw_ctl);
1072dca7c741SRisto Suominen 		if (err < 0)
1073dca7c741SRisto Suominen 			return err;
1074dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card, vmaster_sw);
1075dca7c741SRisto Suominen 		if (err < 0)
1076dca7c741SRisto Suominen 			return err;
1077dca7c741SRisto Suominen 		vmaster_vol = snd_ctl_make_virtual_master(
1078dca7c741SRisto Suominen 			"Master Playback Volume", (unsigned int *) NULL);
1079dca7c741SRisto Suominen 		err = snd_ctl_add_slave(vmaster_vol, master_vol);
1080dca7c741SRisto Suominen 		if (err < 0)
1081dca7c741SRisto Suominen 			return err;
1082dca7c741SRisto Suominen 		err = snd_ctl_add_slave(vmaster_vol, speaker_vol);
1083dca7c741SRisto Suominen 		if (err < 0)
1084dca7c741SRisto Suominen 			return err;
1085dca7c741SRisto Suominen 		err = snd_ctl_add(chip->card, vmaster_vol);
1086dca7c741SRisto Suominen 		if (err < 0)
1087dca7c741SRisto Suominen 			return err;
1088dca7c741SRisto Suominen 	}
1089dca7c741SRisto Suominen 
10904dbf95baSRisto Suominen 	if (beige || g4agp)
1091a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1092a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige),
1093a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost_beige);
1094a8c2a6bfSRisto Suominen 	else if (imac)
1095a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1096a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost_imac),
1097a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost_imac);
1098a8c2a6bfSRisto Suominen 	else if (chip->model == PMAC_SCREAMER)
1099a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1100a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_screamer_mic_boost),
1101a8c2a6bfSRisto Suominen 				snd_pmac_screamer_mic_boost);
1102a8c2a6bfSRisto Suominen 	else if (pm7500)
1103a8c2a6bfSRisto Suominen 		err = build_mixers(chip,
1104a8c2a6bfSRisto Suominen 				ARRAY_SIZE(snd_pmac_awacs_mic_boost_pmac7500),
1105a8c2a6bfSRisto Suominen 				snd_pmac_awacs_mic_boost_pmac7500);
1106a8c2a6bfSRisto Suominen 	else
1107a8c2a6bfSRisto Suominen 		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost),
1108a8c2a6bfSRisto Suominen 				snd_pmac_awacs_mic_boost);
1109a8c2a6bfSRisto Suominen 	if (err < 0)
11101da177e4SLinus Torvalds 		return err;
11111da177e4SLinus Torvalds 
11121da177e4SLinus Torvalds 	/*
11131da177e4SLinus Torvalds 	 * set lowlevel callbacks
11141da177e4SLinus Torvalds 	 */
11151da177e4SLinus Torvalds 	chip->set_format = snd_pmac_awacs_set_format;
11168c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM
11171da177e4SLinus Torvalds 	chip->suspend = snd_pmac_awacs_suspend;
11181da177e4SLinus Torvalds 	chip->resume = snd_pmac_awacs_resume;
11191da177e4SLinus Torvalds #endif
11201da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE
11217ae44cfaSRisto Suominen 	err = snd_pmac_add_automute(chip);
11227ae44cfaSRisto Suominen 	if (err < 0)
11231da177e4SLinus Torvalds 		return err;
11241da177e4SLinus Torvalds 	chip->detect_headphone = snd_pmac_awacs_detect_headphone;
11251da177e4SLinus Torvalds 	chip->update_automute = snd_pmac_awacs_update_automute;
11261da177e4SLinus Torvalds 	snd_pmac_awacs_update_automute(chip, 0); /* update the status only */
11271da177e4SLinus Torvalds #endif
11281da177e4SLinus Torvalds 	if (chip->model == PMAC_SCREAMER) {
11291da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
11301da177e4SLinus Torvalds 		snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
11311da177e4SLinus Torvalds 	}
11321da177e4SLinus Torvalds 
11331da177e4SLinus Torvalds 	return 0;
11341da177e4SLinus Torvalds }
1135