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); 10669ab0cb30STakashi Iwai err = snd_ctl_add_follower_uncached(vmaster_sw, 1067dca7c741SRisto Suominen chip->master_sw_ctl); 1068dca7c741SRisto Suominen if (err < 0) 1069dca7c741SRisto Suominen return err; 10709ab0cb30STakashi Iwai err = snd_ctl_add_follower_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); 10799ab0cb30STakashi Iwai err = snd_ctl_add_follower(vmaster_vol, master_vol); 1080dca7c741SRisto Suominen if (err < 0) 1081dca7c741SRisto Suominen return err; 10829ab0cb30STakashi Iwai err = snd_ctl_add_follower(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