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