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 231da177e4SLinus Torvalds #include <sound/driver.h> 241da177e4SLinus Torvalds #include <asm/io.h> 251da177e4SLinus Torvalds #include <asm/nvram.h> 261da177e4SLinus Torvalds #include <linux/init.h> 271da177e4SLinus Torvalds #include <linux/delay.h> 281da177e4SLinus Torvalds #include <linux/slab.h> 291da177e4SLinus Torvalds #include <sound/core.h> 301da177e4SLinus Torvalds #include "pmac.h" 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds #ifdef CONFIG_ADB_CUDA 341da177e4SLinus Torvalds #define PMAC_AMP_AVAIL 351da177e4SLinus Torvalds #endif 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL 381da177e4SLinus Torvalds typedef struct awacs_amp { 391da177e4SLinus Torvalds unsigned char amp_master; 401da177e4SLinus Torvalds unsigned char amp_vol[2][2]; 411da177e4SLinus Torvalds unsigned char amp_tone[2]; 421da177e4SLinus Torvalds } awacs_amp_t; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds #define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA) 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */ 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds static void snd_pmac_screamer_wait(pmac_t *chip) 501da177e4SLinus Torvalds { 511da177e4SLinus Torvalds long timeout = 2000; 521da177e4SLinus Torvalds while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) { 531da177e4SLinus Torvalds mdelay(1); 541da177e4SLinus Torvalds if (! --timeout) { 551da177e4SLinus Torvalds snd_printd("snd_pmac_screamer_wait timeout\n"); 561da177e4SLinus Torvalds break; 571da177e4SLinus Torvalds } 581da177e4SLinus Torvalds } 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* 621da177e4SLinus Torvalds * write AWACS register 631da177e4SLinus Torvalds */ 641da177e4SLinus Torvalds static void 651da177e4SLinus Torvalds snd_pmac_awacs_write(pmac_t *chip, int val) 661da177e4SLinus Torvalds { 671da177e4SLinus Torvalds long timeout = 5000000; 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds if (chip->model == PMAC_SCREAMER) 701da177e4SLinus Torvalds snd_pmac_screamer_wait(chip); 711da177e4SLinus Torvalds out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22)); 721da177e4SLinus Torvalds while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) { 731da177e4SLinus Torvalds if (! --timeout) { 741da177e4SLinus Torvalds snd_printd("snd_pmac_awacs_write timeout\n"); 751da177e4SLinus Torvalds break; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static void 811da177e4SLinus Torvalds snd_pmac_awacs_write_reg(pmac_t *chip, int reg, int val) 821da177e4SLinus Torvalds { 831da177e4SLinus Torvalds snd_pmac_awacs_write(chip, val | (reg << 12)); 841da177e4SLinus Torvalds chip->awacs_reg[reg] = val; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds static void 881da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(pmac_t *chip, int reg, int val) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds snd_pmac_awacs_write(chip, val | (reg << 12)); 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 938c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM 941da177e4SLinus Torvalds /* Recalibrate chip */ 951da177e4SLinus Torvalds static void screamer_recalibrate(pmac_t *chip) 961da177e4SLinus Torvalds { 971da177e4SLinus Torvalds if (chip->model != PMAC_SCREAMER) 981da177e4SLinus Torvalds return; 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds /* Sorry for the horrible delays... I hope to get that improved 1011da177e4SLinus Torvalds * by making the whole PM process asynchronous in a future version 1021da177e4SLinus Torvalds */ 1031da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); 1041da177e4SLinus Torvalds if (chip->manufacturer == 0x1) 1051da177e4SLinus Torvalds /* delay for broken crystal part */ 106989a0b24SNishanth Aravamudan msleep(750); 1071da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 1, 1081da177e4SLinus Torvalds chip->awacs_reg[1] | MASK_RECALIBRATE | 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 */ 1211da177e4SLinus Torvalds static void snd_pmac_awacs_set_format(pmac_t *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 */ 1351da177e4SLinus Torvalds static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 1361da177e4SLinus Torvalds { 1371da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1381da177e4SLinus Torvalds uinfo->count = 2; 1391da177e4SLinus Torvalds uinfo->value.integer.min = 0; 1401da177e4SLinus Torvalds uinfo->value.integer.max = 15; 1411da177e4SLinus Torvalds return 0; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 1451da177e4SLinus Torvalds { 1461da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 1471da177e4SLinus Torvalds int reg = kcontrol->private_value & 0xff; 1481da177e4SLinus Torvalds int lshift = (kcontrol->private_value >> 8) & 0xff; 1491da177e4SLinus Torvalds int inverted = (kcontrol->private_value >> 16) & 1; 1501da177e4SLinus Torvalds unsigned long flags; 1511da177e4SLinus Torvalds int vol[2]; 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags); 1541da177e4SLinus Torvalds vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf; 1551da177e4SLinus Torvalds vol[1] = chip->awacs_reg[reg] & 0xf; 1561da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags); 1571da177e4SLinus Torvalds if (inverted) { 1581da177e4SLinus Torvalds vol[0] = 0x0f - vol[0]; 1591da177e4SLinus Torvalds vol[1] = 0x0f - vol[1]; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds ucontrol->value.integer.value[0] = vol[0]; 1621da177e4SLinus Torvalds ucontrol->value.integer.value[1] = vol[1]; 1631da177e4SLinus Torvalds return 0; 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 1671da177e4SLinus Torvalds { 1681da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 1691da177e4SLinus Torvalds int reg = kcontrol->private_value & 0xff; 1701da177e4SLinus Torvalds int lshift = (kcontrol->private_value >> 8) & 0xff; 1711da177e4SLinus Torvalds int inverted = (kcontrol->private_value >> 16) & 1; 1721da177e4SLinus Torvalds int val, oldval; 1731da177e4SLinus Torvalds unsigned long flags; 1741da177e4SLinus Torvalds int vol[2]; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds vol[0] = ucontrol->value.integer.value[0]; 1771da177e4SLinus Torvalds vol[1] = ucontrol->value.integer.value[1]; 1781da177e4SLinus Torvalds if (inverted) { 1791da177e4SLinus Torvalds vol[0] = 0x0f - vol[0]; 1801da177e4SLinus Torvalds vol[1] = 0x0f - vol[1]; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds vol[0] &= 0x0f; 1831da177e4SLinus Torvalds vol[1] &= 0x0f; 1841da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags); 1851da177e4SLinus Torvalds oldval = chip->awacs_reg[reg]; 1861da177e4SLinus Torvalds val = oldval & ~(0xf | (0xf << lshift)); 1871da177e4SLinus Torvalds val |= vol[0] << lshift; 1881da177e4SLinus Torvalds val |= vol[1]; 1891da177e4SLinus Torvalds if (oldval != val) 1901da177e4SLinus Torvalds snd_pmac_awacs_write_reg(chip, reg, val); 1911da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags); 1921da177e4SLinus Torvalds return oldval != reg; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds #define AWACS_VOLUME(xname, xreg, xshift, xinverted) \ 1971da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ 1981da177e4SLinus Torvalds .info = snd_pmac_awacs_info_volume, \ 1991da177e4SLinus Torvalds .get = snd_pmac_awacs_get_volume, \ 2001da177e4SLinus Torvalds .put = snd_pmac_awacs_put_volume, \ 2011da177e4SLinus Torvalds .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds /* 2041da177e4SLinus Torvalds * mute master/ogain for AWACS: mono 2051da177e4SLinus Torvalds */ 2061da177e4SLinus Torvalds static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 2091da177e4SLinus Torvalds int reg = kcontrol->private_value & 0xff; 2101da177e4SLinus Torvalds int shift = (kcontrol->private_value >> 8) & 0xff; 2111da177e4SLinus Torvalds int invert = (kcontrol->private_value >> 16) & 1; 2121da177e4SLinus Torvalds int val; 2131da177e4SLinus Torvalds unsigned long flags; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags); 2161da177e4SLinus Torvalds val = (chip->awacs_reg[reg] >> shift) & 1; 2171da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags); 2181da177e4SLinus Torvalds if (invert) 2191da177e4SLinus Torvalds val = 1 - val; 2201da177e4SLinus Torvalds ucontrol->value.integer.value[0] = val; 2211da177e4SLinus Torvalds return 0; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 2251da177e4SLinus Torvalds { 2261da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 2271da177e4SLinus Torvalds int reg = kcontrol->private_value & 0xff; 2281da177e4SLinus Torvalds int shift = (kcontrol->private_value >> 8) & 0xff; 2291da177e4SLinus Torvalds int invert = (kcontrol->private_value >> 16) & 1; 2301da177e4SLinus Torvalds int mask = 1 << shift; 2311da177e4SLinus Torvalds int val, changed; 2321da177e4SLinus Torvalds unsigned long flags; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags); 2351da177e4SLinus Torvalds val = chip->awacs_reg[reg] & ~mask; 2361da177e4SLinus Torvalds if (ucontrol->value.integer.value[0] != invert) 2371da177e4SLinus Torvalds val |= mask; 2381da177e4SLinus Torvalds changed = chip->awacs_reg[reg] != val; 2391da177e4SLinus Torvalds if (changed) 2401da177e4SLinus Torvalds snd_pmac_awacs_write_reg(chip, reg, val); 2411da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags); 2421da177e4SLinus Torvalds return changed; 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds #define AWACS_SWITCH(xname, xreg, xshift, xinvert) \ 2461da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ 2471da177e4SLinus Torvalds .info = snd_pmac_boolean_mono_info, \ 2481da177e4SLinus Torvalds .get = snd_pmac_awacs_get_switch, \ 2491da177e4SLinus Torvalds .put = snd_pmac_awacs_put_switch, \ 2501da177e4SLinus Torvalds .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) } 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL 2541da177e4SLinus Torvalds /* 2551da177e4SLinus Torvalds * controls for perch/whisper extension cards, e.g. G3 desktop 2561da177e4SLinus Torvalds * 2571da177e4SLinus Torvalds * TDA7433 connected via i2c address 0x45 (= 0x8a), 2581da177e4SLinus Torvalds * accessed through cuda 2591da177e4SLinus Torvalds */ 2601da177e4SLinus Torvalds static void awacs_set_cuda(int reg, int val) 2611da177e4SLinus Torvalds { 2621da177e4SLinus Torvalds struct adb_request req; 2631da177e4SLinus Torvalds cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val); 2641da177e4SLinus Torvalds while (! req.complete) 2651da177e4SLinus Torvalds cuda_poll(); 2661da177e4SLinus Torvalds } 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds /* 2691da177e4SLinus Torvalds * level = 0 - 14, 7 = 0 dB 2701da177e4SLinus Torvalds */ 2711da177e4SLinus Torvalds static void awacs_amp_set_tone(awacs_amp_t *amp, int bass, int treble) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds amp->amp_tone[0] = bass; 2741da177e4SLinus Torvalds amp->amp_tone[1] = treble; 2751da177e4SLinus Torvalds if (bass > 7) 2761da177e4SLinus Torvalds bass = (14 - bass) + 8; 2771da177e4SLinus Torvalds if (treble > 7) 2781da177e4SLinus Torvalds treble = (14 - treble) + 8; 2791da177e4SLinus Torvalds awacs_set_cuda(2, (bass << 4) | treble); 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds /* 2831da177e4SLinus Torvalds * vol = 0 - 31 (attenuation), 32 = mute bit, stereo 2841da177e4SLinus Torvalds */ 2851da177e4SLinus Torvalds static int awacs_amp_set_vol(awacs_amp_t *amp, int index, int lvol, int rvol, int do_check) 2861da177e4SLinus Torvalds { 2871da177e4SLinus Torvalds if (do_check && amp->amp_vol[index][0] == lvol && 2881da177e4SLinus Torvalds amp->amp_vol[index][1] == rvol) 2891da177e4SLinus Torvalds return 0; 2901da177e4SLinus Torvalds awacs_set_cuda(3 + index, lvol); 2911da177e4SLinus Torvalds awacs_set_cuda(5 + index, rvol); 2921da177e4SLinus Torvalds amp->amp_vol[index][0] = lvol; 2931da177e4SLinus Torvalds amp->amp_vol[index][1] = rvol; 2941da177e4SLinus Torvalds return 1; 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds /* 2981da177e4SLinus Torvalds * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB 2991da177e4SLinus Torvalds */ 3001da177e4SLinus Torvalds static void awacs_amp_set_master(awacs_amp_t *amp, int vol) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds amp->amp_master = vol; 3031da177e4SLinus Torvalds if (vol <= 79) 3041da177e4SLinus Torvalds vol = 32 + (79 - vol); 3051da177e4SLinus Torvalds else 3061da177e4SLinus Torvalds vol = 32 - (vol - 79); 3071da177e4SLinus Torvalds awacs_set_cuda(1, vol); 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds static void awacs_amp_free(pmac_t *chip) 3111da177e4SLinus Torvalds { 3121da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 3131da177e4SLinus Torvalds snd_assert(amp, return); 3141da177e4SLinus Torvalds kfree(amp); 3151da177e4SLinus Torvalds chip->mixer_data = NULL; 3161da177e4SLinus Torvalds chip->mixer_free = NULL; 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds /* 3211da177e4SLinus Torvalds * mixer controls 3221da177e4SLinus Torvalds */ 3231da177e4SLinus Torvalds static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 3241da177e4SLinus Torvalds { 3251da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3261da177e4SLinus Torvalds uinfo->count = 2; 3271da177e4SLinus Torvalds uinfo->value.integer.min = 0; 3281da177e4SLinus Torvalds uinfo->value.integer.max = 31; 3291da177e4SLinus Torvalds return 0; 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds static int snd_pmac_awacs_get_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 3331da177e4SLinus Torvalds { 3341da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 3351da177e4SLinus Torvalds int index = kcontrol->private_value; 3361da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 3371da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 3381da177e4SLinus Torvalds snd_assert(index >= 0 && index <= 1, return -EINVAL); 3391da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31); 3401da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31); 3411da177e4SLinus Torvalds return 0; 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds static int snd_pmac_awacs_put_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 3451da177e4SLinus Torvalds { 3461da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 3471da177e4SLinus Torvalds int index = kcontrol->private_value; 3481da177e4SLinus Torvalds int vol[2]; 3491da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 3501da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 3511da177e4SLinus Torvalds snd_assert(index >= 0 && index <= 1, return -EINVAL); 3521da177e4SLinus Torvalds 3531da177e4SLinus Torvalds vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32); 3541da177e4SLinus Torvalds vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32); 3551da177e4SLinus Torvalds return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds static int snd_pmac_awacs_get_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 3591da177e4SLinus Torvalds { 3601da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 3611da177e4SLinus Torvalds int index = kcontrol->private_value; 3621da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 3631da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 3641da177e4SLinus Torvalds snd_assert(index >= 0 && index <= 1, return -EINVAL); 3651da177e4SLinus Torvalds ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1; 3661da177e4SLinus Torvalds ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1; 3671da177e4SLinus Torvalds return 0; 3681da177e4SLinus Torvalds } 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds static int snd_pmac_awacs_put_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 3711da177e4SLinus Torvalds { 3721da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 3731da177e4SLinus Torvalds int index = kcontrol->private_value; 3741da177e4SLinus Torvalds int vol[2]; 3751da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 3761da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 3771da177e4SLinus Torvalds snd_assert(index >= 0 && index <= 1, return -EINVAL); 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31); 3801da177e4SLinus Torvalds vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31); 3811da177e4SLinus Torvalds return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); 3821da177e4SLinus Torvalds } 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds static int snd_pmac_awacs_info_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 3851da177e4SLinus Torvalds { 3861da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3871da177e4SLinus Torvalds uinfo->count = 1; 3881da177e4SLinus Torvalds uinfo->value.integer.min = 0; 3891da177e4SLinus Torvalds uinfo->value.integer.max = 14; 3901da177e4SLinus Torvalds return 0; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds static int snd_pmac_awacs_get_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 3941da177e4SLinus Torvalds { 3951da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 3961da177e4SLinus Torvalds int index = kcontrol->private_value; 3971da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 3981da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 3991da177e4SLinus Torvalds snd_assert(index >= 0 && index <= 1, return -EINVAL); 4001da177e4SLinus Torvalds ucontrol->value.integer.value[0] = amp->amp_tone[index]; 4011da177e4SLinus Torvalds return 0; 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds 4041da177e4SLinus Torvalds static int snd_pmac_awacs_put_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 4051da177e4SLinus Torvalds { 4061da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 4071da177e4SLinus Torvalds int index = kcontrol->private_value; 4081da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 4091da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 4101da177e4SLinus Torvalds snd_assert(index >= 0 && index <= 1, return -EINVAL); 4111da177e4SLinus Torvalds if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) { 4121da177e4SLinus Torvalds amp->amp_tone[index] = ucontrol->value.integer.value[0]; 4131da177e4SLinus Torvalds awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); 4141da177e4SLinus Torvalds return 1; 4151da177e4SLinus Torvalds } 4161da177e4SLinus Torvalds return 0; 4171da177e4SLinus Torvalds } 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds static int snd_pmac_awacs_info_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 4201da177e4SLinus Torvalds { 4211da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4221da177e4SLinus Torvalds uinfo->count = 1; 4231da177e4SLinus Torvalds uinfo->value.integer.min = 0; 4241da177e4SLinus Torvalds uinfo->value.integer.max = 99; 4251da177e4SLinus Torvalds return 0; 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds static int snd_pmac_awacs_get_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 4291da177e4SLinus Torvalds { 4301da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 4311da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 4321da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 4331da177e4SLinus Torvalds ucontrol->value.integer.value[0] = amp->amp_master; 4341da177e4SLinus Torvalds return 0; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds static int snd_pmac_awacs_put_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 4381da177e4SLinus Torvalds { 4391da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 4401da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 4411da177e4SLinus Torvalds snd_assert(amp, return -EINVAL); 4421da177e4SLinus Torvalds if (ucontrol->value.integer.value[0] != amp->amp_master) { 4431da177e4SLinus Torvalds amp->amp_master = ucontrol->value.integer.value[0]; 4441da177e4SLinus Torvalds awacs_amp_set_master(amp, amp->amp_master); 4451da177e4SLinus Torvalds return 1; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds return 0; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds #define AMP_CH_SPK 0 4511da177e4SLinus Torvalds #define AMP_CH_HD 1 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_amp_vol[] __initdata = { 4541da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4551da177e4SLinus Torvalds .name = "PC Speaker Playback Volume", 4561da177e4SLinus Torvalds .info = snd_pmac_awacs_info_volume_amp, 4571da177e4SLinus Torvalds .get = snd_pmac_awacs_get_volume_amp, 4581da177e4SLinus Torvalds .put = snd_pmac_awacs_put_volume_amp, 4591da177e4SLinus Torvalds .private_value = AMP_CH_SPK, 4601da177e4SLinus Torvalds }, 4611da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4621da177e4SLinus Torvalds .name = "Headphone Playback Volume", 4631da177e4SLinus Torvalds .info = snd_pmac_awacs_info_volume_amp, 4641da177e4SLinus Torvalds .get = snd_pmac_awacs_get_volume_amp, 4651da177e4SLinus Torvalds .put = snd_pmac_awacs_put_volume_amp, 4661da177e4SLinus Torvalds .private_value = AMP_CH_HD, 4671da177e4SLinus Torvalds }, 4681da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4691da177e4SLinus Torvalds .name = "Tone Control - Bass", 4701da177e4SLinus Torvalds .info = snd_pmac_awacs_info_tone_amp, 4711da177e4SLinus Torvalds .get = snd_pmac_awacs_get_tone_amp, 4721da177e4SLinus Torvalds .put = snd_pmac_awacs_put_tone_amp, 4731da177e4SLinus Torvalds .private_value = 0, 4741da177e4SLinus Torvalds }, 4751da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4761da177e4SLinus Torvalds .name = "Tone Control - Treble", 4771da177e4SLinus Torvalds .info = snd_pmac_awacs_info_tone_amp, 4781da177e4SLinus Torvalds .get = snd_pmac_awacs_get_tone_amp, 4791da177e4SLinus Torvalds .put = snd_pmac_awacs_put_tone_amp, 4801da177e4SLinus Torvalds .private_value = 1, 4811da177e4SLinus Torvalds }, 4821da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4831da177e4SLinus Torvalds .name = "Amp Master Playback Volume", 4841da177e4SLinus Torvalds .info = snd_pmac_awacs_info_master_amp, 4851da177e4SLinus Torvalds .get = snd_pmac_awacs_get_master_amp, 4861da177e4SLinus Torvalds .put = snd_pmac_awacs_put_master_amp, 4871da177e4SLinus Torvalds }, 4881da177e4SLinus Torvalds }; 4891da177e4SLinus Torvalds 4901da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_amp_hp_sw __initdata = { 4911da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4921da177e4SLinus Torvalds .name = "Headphone Playback Switch", 4931da177e4SLinus Torvalds .info = snd_pmac_boolean_stereo_info, 4941da177e4SLinus Torvalds .get = snd_pmac_awacs_get_switch_amp, 4951da177e4SLinus Torvalds .put = snd_pmac_awacs_put_switch_amp, 4961da177e4SLinus Torvalds .private_value = AMP_CH_HD, 4971da177e4SLinus Torvalds }; 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_amp_spk_sw __initdata = { 5001da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5011da177e4SLinus Torvalds .name = "PC Speaker Playback Switch", 5021da177e4SLinus Torvalds .info = snd_pmac_boolean_stereo_info, 5031da177e4SLinus Torvalds .get = snd_pmac_awacs_get_switch_amp, 5041da177e4SLinus Torvalds .put = snd_pmac_awacs_put_switch_amp, 5051da177e4SLinus Torvalds .private_value = AMP_CH_SPK, 5061da177e4SLinus Torvalds }; 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */ 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds /* 5121da177e4SLinus Torvalds * mic boost for screamer 5131da177e4SLinus Torvalds */ 5141da177e4SLinus Torvalds static int snd_pmac_screamer_mic_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 5151da177e4SLinus Torvalds { 5161da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 5171da177e4SLinus Torvalds uinfo->count = 1; 5181da177e4SLinus Torvalds uinfo->value.integer.min = 0; 5191da177e4SLinus Torvalds uinfo->value.integer.max = 2; 5201da177e4SLinus Torvalds return 0; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds static int snd_pmac_screamer_mic_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 5241da177e4SLinus Torvalds { 5251da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 5261da177e4SLinus Torvalds int val; 5271da177e4SLinus Torvalds unsigned long flags; 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags); 5301da177e4SLinus Torvalds if (chip->awacs_reg[6] & MASK_MIC_BOOST) 5311da177e4SLinus Torvalds val = 2; 5321da177e4SLinus Torvalds else if (chip->awacs_reg[0] & MASK_GAINLINE) 5331da177e4SLinus Torvalds val = 1; 5341da177e4SLinus Torvalds else 5351da177e4SLinus Torvalds val = 0; 5361da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags); 5371da177e4SLinus Torvalds ucontrol->value.integer.value[0] = val; 5381da177e4SLinus Torvalds return 0; 5391da177e4SLinus Torvalds } 5401da177e4SLinus Torvalds 5411da177e4SLinus Torvalds static int snd_pmac_screamer_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 5421da177e4SLinus Torvalds { 5431da177e4SLinus Torvalds pmac_t *chip = snd_kcontrol_chip(kcontrol); 5441da177e4SLinus Torvalds int changed = 0; 5451da177e4SLinus Torvalds int val0, val6; 5461da177e4SLinus Torvalds unsigned long flags; 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags); 5491da177e4SLinus Torvalds val0 = chip->awacs_reg[0] & ~MASK_GAINLINE; 5501da177e4SLinus Torvalds val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST; 5511da177e4SLinus Torvalds if (ucontrol->value.integer.value[0] > 0) { 5521da177e4SLinus Torvalds val0 |= MASK_GAINLINE; 5531da177e4SLinus Torvalds if (ucontrol->value.integer.value[0] > 1) 5541da177e4SLinus Torvalds val6 |= MASK_MIC_BOOST; 5551da177e4SLinus Torvalds } 5561da177e4SLinus Torvalds if (val0 != chip->awacs_reg[0]) { 5571da177e4SLinus Torvalds snd_pmac_awacs_write_reg(chip, 0, val0); 5581da177e4SLinus Torvalds changed = 1; 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds if (val6 != chip->awacs_reg[6]) { 5611da177e4SLinus Torvalds snd_pmac_awacs_write_reg(chip, 6, val6); 5621da177e4SLinus Torvalds changed = 1; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags); 5651da177e4SLinus Torvalds return changed; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds 5681da177e4SLinus Torvalds /* 5691da177e4SLinus Torvalds * lists of mixer elements 5701da177e4SLinus Torvalds */ 5711da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_mixers[] __initdata = { 5721da177e4SLinus Torvalds AWACS_VOLUME("Master Playback Volume", 2, 6, 1), 5731da177e4SLinus Torvalds AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0), 5741da177e4SLinus Torvalds AWACS_VOLUME("Capture Volume", 0, 4, 0), 5751da177e4SLinus Torvalds AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), 5761da177e4SLinus Torvalds }; 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds /* FIXME: is this correct order? 5791da177e4SLinus Torvalds * screamer (powerbook G3 pismo) seems to have different bits... 5801da177e4SLinus Torvalds */ 5811da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_mixers2[] __initdata = { 5821da177e4SLinus Torvalds AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0), 5831da177e4SLinus Torvalds AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0), 5841da177e4SLinus Torvalds }; 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_screamer_mixers2[] __initdata = { 5871da177e4SLinus Torvalds AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), 5881da177e4SLinus Torvalds AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0), 5891da177e4SLinus Torvalds }; 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_master_sw __initdata = 5921da177e4SLinus Torvalds AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1); 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_mic_boost[] __initdata = { 5951da177e4SLinus Torvalds AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0), 5961da177e4SLinus Torvalds }; 5971da177e4SLinus Torvalds 5981da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_screamer_mic_boost[] __initdata = { 5991da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 6001da177e4SLinus Torvalds .name = "Mic Boost", 6011da177e4SLinus Torvalds .info = snd_pmac_screamer_mic_boost_info, 6021da177e4SLinus Torvalds .get = snd_pmac_screamer_mic_boost_get, 6031da177e4SLinus Torvalds .put = snd_pmac_screamer_mic_boost_put, 6041da177e4SLinus Torvalds }, 6051da177e4SLinus Torvalds }; 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_speaker_vol[] __initdata = { 6081da177e4SLinus Torvalds AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1), 6091da177e4SLinus Torvalds }; 6101da177e4SLinus Torvalds static snd_kcontrol_new_t snd_pmac_awacs_speaker_sw __initdata = 6111da177e4SLinus Torvalds AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds /* 6151da177e4SLinus Torvalds * add new mixer elements to the card 6161da177e4SLinus Torvalds */ 6171da177e4SLinus Torvalds static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers) 6181da177e4SLinus Torvalds { 6191da177e4SLinus Torvalds int i, err; 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds for (i = 0; i < nums; i++) { 6221da177e4SLinus Torvalds if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) 6231da177e4SLinus Torvalds return err; 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds return 0; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds 6291da177e4SLinus Torvalds /* 6301da177e4SLinus Torvalds * restore all registers 6311da177e4SLinus Torvalds */ 6321da177e4SLinus Torvalds static void awacs_restore_all_regs(pmac_t *chip) 6331da177e4SLinus Torvalds { 6341da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); 6351da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); 6361da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]); 6371da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]); 6381da177e4SLinus Torvalds if (chip->model == PMAC_SCREAMER) { 6391da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]); 6401da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); 6411da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]); 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds 6458c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM 6461da177e4SLinus Torvalds static void snd_pmac_awacs_suspend(pmac_t *chip) 6471da177e4SLinus Torvalds { 6481da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1] 6491da177e4SLinus Torvalds | MASK_AMUTE | MASK_CMUTE)); 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds static void snd_pmac_awacs_resume(pmac_t *chip) 6531da177e4SLinus Torvalds { 6541da177e4SLinus Torvalds if (machine_is_compatible("PowerBook3,1") 6551da177e4SLinus Torvalds || machine_is_compatible("PowerBook3,2")) { 656989a0b24SNishanth Aravamudan msleep(100); 6571da177e4SLinus Torvalds snd_pmac_awacs_write_reg(chip, 1, 6581da177e4SLinus Torvalds chip->awacs_reg[1] & ~MASK_PAROUT); 659989a0b24SNishanth Aravamudan msleep(300); 6601da177e4SLinus Torvalds } 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds awacs_restore_all_regs(chip); 6631da177e4SLinus Torvalds if (chip->model == PMAC_SCREAMER) { 6641da177e4SLinus Torvalds /* reset power bits in reg 6 */ 6651da177e4SLinus Torvalds mdelay(5); 6661da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); 6671da177e4SLinus Torvalds } 6681da177e4SLinus Torvalds screamer_recalibrate(chip); 6691da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL 6701da177e4SLinus Torvalds if (chip->mixer_data) { 6711da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 6721da177e4SLinus Torvalds awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0); 6731da177e4SLinus Torvalds awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0); 6741da177e4SLinus Torvalds awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); 6751da177e4SLinus Torvalds awacs_amp_set_master(amp, amp->amp_master); 6761da177e4SLinus Torvalds } 6771da177e4SLinus Torvalds #endif 6781da177e4SLinus Torvalds } 6798c870933SBenjamin Herrenschmidt #endif /* CONFIG_PM */ 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE 6821da177e4SLinus Torvalds /* 6831da177e4SLinus Torvalds * auto-mute stuffs 6841da177e4SLinus Torvalds */ 6851da177e4SLinus Torvalds static int snd_pmac_awacs_detect_headphone(pmac_t *chip) 6861da177e4SLinus Torvalds { 6871da177e4SLinus Torvalds return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds 6901da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL 6911da177e4SLinus Torvalds static int toggle_amp_mute(awacs_amp_t *amp, int index, int mute) 6921da177e4SLinus Torvalds { 6931da177e4SLinus Torvalds int vol[2]; 6941da177e4SLinus Torvalds vol[0] = amp->amp_vol[index][0] & 31; 6951da177e4SLinus Torvalds vol[1] = amp->amp_vol[index][1] & 31; 6961da177e4SLinus Torvalds if (mute) { 6971da177e4SLinus Torvalds vol[0] |= 32; 6981da177e4SLinus Torvalds vol[1] |= 32; 6991da177e4SLinus Torvalds } 7001da177e4SLinus Torvalds return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds #endif 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds static void snd_pmac_awacs_update_automute(pmac_t *chip, int do_notify) 7051da177e4SLinus Torvalds { 7061da177e4SLinus Torvalds if (chip->auto_mute) { 7071da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL 7081da177e4SLinus Torvalds if (chip->mixer_data) { 7091da177e4SLinus Torvalds awacs_amp_t *amp = chip->mixer_data; 7101da177e4SLinus Torvalds int changed; 7111da177e4SLinus Torvalds if (snd_pmac_awacs_detect_headphone(chip)) { 7121da177e4SLinus Torvalds changed = toggle_amp_mute(amp, AMP_CH_HD, 0); 7131da177e4SLinus Torvalds changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1); 7141da177e4SLinus Torvalds } else { 7151da177e4SLinus Torvalds changed = toggle_amp_mute(amp, AMP_CH_HD, 1); 7161da177e4SLinus Torvalds changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0); 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds if (do_notify && ! changed) 7191da177e4SLinus Torvalds return; 7201da177e4SLinus Torvalds } else 7211da177e4SLinus Torvalds #endif 7221da177e4SLinus Torvalds { 7231da177e4SLinus Torvalds int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE); 7241da177e4SLinus Torvalds if (snd_pmac_awacs_detect_headphone(chip)) 7251da177e4SLinus Torvalds reg &= ~MASK_HDMUTE; 7261da177e4SLinus Torvalds else 7271da177e4SLinus Torvalds reg &= ~MASK_SPKMUTE; 7281da177e4SLinus Torvalds if (do_notify && reg == chip->awacs_reg[1]) 7291da177e4SLinus Torvalds return; 7301da177e4SLinus Torvalds snd_pmac_awacs_write_reg(chip, 1, reg); 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds if (do_notify) { 7331da177e4SLinus Torvalds snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, 7341da177e4SLinus Torvalds &chip->master_sw_ctl->id); 7351da177e4SLinus Torvalds snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, 7361da177e4SLinus Torvalds &chip->speaker_sw_ctl->id); 7371da177e4SLinus Torvalds snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, 7381da177e4SLinus Torvalds &chip->hp_detect_ctl->id); 7391da177e4SLinus Torvalds } 7401da177e4SLinus Torvalds } 7411da177e4SLinus Torvalds } 7421da177e4SLinus Torvalds #endif /* PMAC_SUPPORT_AUTOMUTE */ 7431da177e4SLinus Torvalds 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds /* 7461da177e4SLinus Torvalds * initialize chip 7471da177e4SLinus Torvalds */ 7481da177e4SLinus Torvalds int __init 7491da177e4SLinus Torvalds snd_pmac_awacs_init(pmac_t *chip) 7501da177e4SLinus Torvalds { 7511da177e4SLinus Torvalds int err, vol; 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds /* looks like MASK_GAINLINE triggers something, so we set here 7541da177e4SLinus Torvalds * as start-up 7551da177e4SLinus Torvalds */ 7561da177e4SLinus Torvalds chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE; 7571da177e4SLinus Torvalds chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE; 7581da177e4SLinus Torvalds /* FIXME: Only machines with external SRS module need MASK_PAROUT */ 7591da177e4SLinus Torvalds if (chip->has_iic || chip->device_id == 0x5 || 7601da177e4SLinus Torvalds /*chip->_device_id == 0x8 || */ 7611da177e4SLinus Torvalds chip->device_id == 0xb) 7621da177e4SLinus Torvalds chip->awacs_reg[1] |= MASK_PAROUT; 7631da177e4SLinus Torvalds /* get default volume from nvram */ 7641da177e4SLinus Torvalds // vol = (~nvram_read_byte(0x1308) & 7) << 1; 7651da177e4SLinus Torvalds // vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 ); 7661da177e4SLinus Torvalds vol = 0x0f; /* no, on alsa, muted as default */ 7671da177e4SLinus Torvalds vol = vol + (vol << 6); 7681da177e4SLinus Torvalds chip->awacs_reg[2] = vol; 7691da177e4SLinus Torvalds chip->awacs_reg[4] = vol; 7701da177e4SLinus Torvalds if (chip->model == PMAC_SCREAMER) { 7711da177e4SLinus Torvalds chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */ 7721da177e4SLinus Torvalds chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ 7731da177e4SLinus Torvalds chip->awacs_reg[7] = 0; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds 7761da177e4SLinus Torvalds awacs_restore_all_regs(chip); 7771da177e4SLinus Torvalds chip->manufacturer = (in_le32(&chip->awacs->codec_stat) >> 8) & 0xf; 7781da177e4SLinus Torvalds screamer_recalibrate(chip); 7791da177e4SLinus Torvalds 7801da177e4SLinus Torvalds chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf; 7811da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL 7821da177e4SLinus Torvalds if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) { 7831da177e4SLinus Torvalds awacs_amp_t *amp = kmalloc(sizeof(*amp), GFP_KERNEL); 7841da177e4SLinus Torvalds if (! amp) 7851da177e4SLinus Torvalds return -ENOMEM; 7861da177e4SLinus Torvalds chip->mixer_data = amp; 7871da177e4SLinus Torvalds memset(amp, 0, sizeof(*amp)); 7881da177e4SLinus Torvalds chip->mixer_free = awacs_amp_free; 7891da177e4SLinus Torvalds awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */ 7901da177e4SLinus Torvalds awacs_amp_set_vol(amp, 1, 63, 63, 0); 7911da177e4SLinus Torvalds awacs_amp_set_tone(amp, 7, 7); /* 0 dB */ 7921da177e4SLinus Torvalds awacs_amp_set_master(amp, 79); /* 0 dB */ 7931da177e4SLinus Torvalds } 7941da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */ 7951da177e4SLinus Torvalds 7961da177e4SLinus Torvalds if (chip->hp_stat_mask == 0) { 7971da177e4SLinus Torvalds /* set headphone-jack detection bit */ 7981da177e4SLinus Torvalds switch (chip->model) { 7991da177e4SLinus Torvalds case PMAC_AWACS: 8001da177e4SLinus Torvalds chip->hp_stat_mask = 0x04; 8011da177e4SLinus Torvalds break; 8021da177e4SLinus Torvalds case PMAC_SCREAMER: 8031da177e4SLinus Torvalds switch (chip->device_id) { 8041da177e4SLinus Torvalds case 0x08: 8051da177e4SLinus Torvalds /* 1 = side jack, 2 = front jack */ 8061da177e4SLinus Torvalds chip->hp_stat_mask = 0x03; 8071da177e4SLinus Torvalds break; 8081da177e4SLinus Torvalds case 0x00: 8091da177e4SLinus Torvalds case 0x05: 8101da177e4SLinus Torvalds chip->hp_stat_mask = 0x04; 8111da177e4SLinus Torvalds break; 8121da177e4SLinus Torvalds default: 8131da177e4SLinus Torvalds chip->hp_stat_mask = 0x08; 8141da177e4SLinus Torvalds break; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds break; 8171da177e4SLinus Torvalds default: 8181da177e4SLinus Torvalds snd_BUG(); 8191da177e4SLinus Torvalds break; 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds } 8221da177e4SLinus Torvalds 8231da177e4SLinus Torvalds /* 8241da177e4SLinus Torvalds * build mixers 8251da177e4SLinus Torvalds */ 8261da177e4SLinus Torvalds strcpy(chip->card->mixername, "PowerMac AWACS"); 8271da177e4SLinus Torvalds 8281da177e4SLinus Torvalds if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), 8291da177e4SLinus Torvalds snd_pmac_awacs_mixers)) < 0) 8301da177e4SLinus Torvalds return err; 8311da177e4SLinus Torvalds if (chip->model == PMAC_SCREAMER) 8321da177e4SLinus Torvalds err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2), 8331da177e4SLinus Torvalds snd_pmac_screamer_mixers2); 8341da177e4SLinus Torvalds else 8351da177e4SLinus Torvalds err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2), 8361da177e4SLinus Torvalds snd_pmac_awacs_mixers2); 8371da177e4SLinus Torvalds if (err < 0) 8381da177e4SLinus Torvalds return err; 8391da177e4SLinus Torvalds chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip); 8401da177e4SLinus Torvalds if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) 8411da177e4SLinus Torvalds return err; 8421da177e4SLinus Torvalds #ifdef PMAC_AMP_AVAIL 8431da177e4SLinus Torvalds if (chip->mixer_data) { 8441da177e4SLinus Torvalds /* use amplifier. the signal is connected from route A 8451da177e4SLinus Torvalds * to the amp. the amp has its headphone and speaker 8461da177e4SLinus Torvalds * volumes and mute switches, so we use them instead of 8471da177e4SLinus Torvalds * screamer registers. 8481da177e4SLinus Torvalds * in this case, it seems the route C is not used. 8491da177e4SLinus Torvalds */ 8501da177e4SLinus Torvalds if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), 8511da177e4SLinus Torvalds snd_pmac_awacs_amp_vol)) < 0) 8521da177e4SLinus Torvalds return err; 8531da177e4SLinus Torvalds /* overwrite */ 8541da177e4SLinus Torvalds chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip); 8551da177e4SLinus Torvalds if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) 8561da177e4SLinus Torvalds return err; 8571da177e4SLinus Torvalds chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip); 8581da177e4SLinus Torvalds if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) 8591da177e4SLinus Torvalds return err; 8601da177e4SLinus Torvalds } else 8611da177e4SLinus Torvalds #endif /* PMAC_AMP_AVAIL */ 8621da177e4SLinus Torvalds { 8631da177e4SLinus Torvalds /* route A = headphone, route C = speaker */ 8641da177e4SLinus Torvalds if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol), 8651da177e4SLinus Torvalds snd_pmac_awacs_speaker_vol)) < 0) 8661da177e4SLinus Torvalds return err; 8671da177e4SLinus Torvalds chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip); 8681da177e4SLinus Torvalds if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) 8691da177e4SLinus Torvalds return err; 8701da177e4SLinus Torvalds } 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds if (chip->model == PMAC_SCREAMER) { 8731da177e4SLinus Torvalds if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost), 8741da177e4SLinus Torvalds snd_pmac_screamer_mic_boost)) < 0) 8751da177e4SLinus Torvalds return err; 8761da177e4SLinus Torvalds } else { 8771da177e4SLinus Torvalds if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), 8781da177e4SLinus Torvalds snd_pmac_awacs_mic_boost)) < 0) 8791da177e4SLinus Torvalds return err; 8801da177e4SLinus Torvalds } 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* 8831da177e4SLinus Torvalds * set lowlevel callbacks 8841da177e4SLinus Torvalds */ 8851da177e4SLinus Torvalds chip->set_format = snd_pmac_awacs_set_format; 8868c870933SBenjamin Herrenschmidt #ifdef CONFIG_PM 8871da177e4SLinus Torvalds chip->suspend = snd_pmac_awacs_suspend; 8881da177e4SLinus Torvalds chip->resume = snd_pmac_awacs_resume; 8891da177e4SLinus Torvalds #endif 8901da177e4SLinus Torvalds #ifdef PMAC_SUPPORT_AUTOMUTE 8911da177e4SLinus Torvalds if ((err = snd_pmac_add_automute(chip)) < 0) 8921da177e4SLinus Torvalds return err; 8931da177e4SLinus Torvalds chip->detect_headphone = snd_pmac_awacs_detect_headphone; 8941da177e4SLinus Torvalds chip->update_automute = snd_pmac_awacs_update_automute; 8951da177e4SLinus Torvalds snd_pmac_awacs_update_automute(chip, 0); /* update the status only */ 8961da177e4SLinus Torvalds #endif 8971da177e4SLinus Torvalds if (chip->model == PMAC_SCREAMER) { 8981da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); 8991da177e4SLinus Torvalds snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); 9001da177e4SLinus Torvalds } 9011da177e4SLinus Torvalds 9021da177e4SLinus Torvalds return 0; 9031da177e4SLinus Torvalds } 904